http_inspect = { }
http2_inspect = { }
imap = { }
+iec104 = { }
modbus = { }
netflow = {}
normalizer = { }
{ when = { proto = 'tcp', ports = '111', role='server' }, use = { type = 'rpc_decode' } },
{ when = { proto = 'tcp', ports = '502', role='server' }, use = { type = 'modbus' } },
{ when = { proto = 'tcp', ports = '2123 2152 3386', role='server' }, use = { type = 'gtp_inspect' } },
+ { when = { proto = 'tcp', ports = '2404', role='server' }, use = { type = 'iec104' } },
{ when = { proto = 'tcp', service = 'dcerpc' }, use = { type = 'dce_tcp' } },
{ when = { proto = 'udp', service = 'dcerpc' }, use = { type = 'dce_udp' } },
{ when = { service = 'imap' }, use = { type = 'imap' } },
{ when = { service = 'http' }, use = { type = 'http_inspect' } },
{ when = { service = 'http2' }, use = { type = 'http2_inspect' } },
+ { when = { service = 'iec104' }, use = { type = 'iec104' } },
{ when = { service = 'modbus' }, use = { type = 'modbus' } },
{ when = { service = 'pop3' }, use = { type = 'pop' } },
{ when = { service = 'ssh' }, use = { type = 'ssh' } },
add_subdirectory(gtp)
add_subdirectory(http_inspect)
add_subdirectory(http2_inspect)
+add_subdirectory(iec104)
add_subdirectory(imap)
add_subdirectory(modbus)
add_subdirectory(netflow)
$<TARGET_OBJECTS:dns>
$<TARGET_OBJECTS:ftp_telnet>
$<TARGET_OBJECTS:gtp_inspect>
+ $<TARGET_OBJECTS:iec104>
$<TARGET_OBJECTS:imap>
$<TARGET_OBJECTS:modbus>
$<TARGET_OBJECTS:netflow>
--- /dev/null
+set( FILE_LIST
+ iec104.cc
+ iec104.h
+ iec104_decode.cc
+ iec104_decode.h
+ iec104_module.cc
+ iec104_module.h
+ iec104_paf.cc
+ iec104_paf.h
+ iec104_parse_apdu.cc
+ iec104_parse_apdu.h
+ iec104_parse_information_object_elements.cc
+ iec104_parse_information_object_elements.h
+ iec104_trace.h
+ ips_iec104_apci_type.cc
+ ips_iec104_asdu_func.cc
+)
+
+if (STATIC_INSPECTORS)
+ add_library(iec104 OBJECT ${FILE_LIST})
+
+else (STATIC_INSPECTORS)
+ add_dynamic_module(iec104 inspectors ${FILE_LIST})
+
+endif (STATIC_INSPECTORS)
+
--- /dev/null
+IEC 60870-5-104 (IEC104) is a protocol distributed by the International
+Electrotechnical Commission (IEC) that provides a standardized method of
+sending telecontrol messages between central stations and outstations,
+typically running on TCP port 2404.
+
+It is used in combination with the companion specifications in the
+IEC 60870-5 family, most notably IEC 60870-5-101, to provide reliable
+transport via TCP/IP.
+
+An IEC104 Application Protocol Data Unit (APDU) consists of one of three
+Application Protocol Control Information (APCI) structures, each beginning
+with the start byte 0x68. In the case of an Information Transfer APCI, an
+Application Service Data Unit (ASDU) follows the APCI.
+
+The IEC104 inspector decodes the IEC104 protocol and provides rule options
+to access certain protocol fields and data content. This allows the user to
+write rules for IEC104 packets without decoding the protocol.
\ No newline at end of file
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus.cc (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm.cc (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iec104.h"
+
+#include "detection/detection_engine.h"
+#include "events/event_queue.h"
+#include "profiler/profiler.h"
+#include "protocols/packet.h"
+
+#include "iec104_decode.h"
+#include "iec104_module.h"
+#include "iec104_paf.h"
+
+using namespace snort;
+
+THREAD_LOCAL Iec104Stats iec104_stats;
+
+//-------------------------------------------------------------------------
+// flow stuff
+//-------------------------------------------------------------------------
+
+unsigned Iec104FlowData::inspector_id = 0;
+
+void Iec104FlowData::init()
+{
+ inspector_id = FlowData::create_flow_data_id();
+}
+
+Iec104FlowData::Iec104FlowData() :
+ FlowData(inspector_id)
+{
+ iec104_stats.concurrent_sessions++;
+ if (iec104_stats.max_concurrent_sessions < iec104_stats.concurrent_sessions)
+ {
+ iec104_stats.max_concurrent_sessions = iec104_stats.concurrent_sessions;
+ }
+}
+
+Iec104FlowData::~Iec104FlowData()
+{
+ assert(iec104_stats.concurrent_sessions > 0);
+ iec104_stats.concurrent_sessions--;
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Iec104: public Inspector
+{
+public:
+ // default ctor / dtor
+ void eval(Packet*) override;
+
+ uint32_t get_message_type(uint32_t version, const char* name);
+ uint32_t get_info_type(uint32_t version, const char* name);
+
+ StreamSplitter* get_splitter(bool c2s) override
+ {
+ return new Iec104Splitter(c2s);
+ }
+};
+
+void Iec104::eval(Packet* p)
+{
+ Profile profile(iec104_prof);
+
+ // preconditions - what we registered for
+ assert(p->has_tcp_data());
+
+ Iec104FlowData* iec104fd = (Iec104FlowData*) p->flow->get_flow_data(Iec104FlowData::inspector_id);
+
+ if (!p->is_full_pdu())
+ {
+ if (iec104fd)
+ {
+ iec104fd->reset();
+ }
+
+ // If a packet is rebuilt, but not a full PDU, then it's garbage that
+ // got flushed at the end of a stream.
+ if (p->packet_flags & (PKT_REBUILT_STREAM | PKT_PDU_HEAD))
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_LENGTH);
+ }
+
+ return;
+ }
+
+ if (!iec104fd)
+ {
+ iec104fd = new Iec104FlowData;
+ p->flow->set_flow_data(iec104fd);
+ iec104_stats.sessions++;
+ }
+
+ // verify that the reported message length is at least the minimum size
+ if ((p->data[1] < IEC104_MIN_APCI_LEN))
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_LENGTH);
+ }
+
+ // update stats
+ iec104_stats.frames++;
+
+ // When pipelined Iec104 PDUs appear in a single TCP segment, the
+ // detection engine caches the results of the rule options after
+ // evaluating on the first PDU. Setting this flag stops the caching.
+ p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT;
+
+ if (!Iec104Decode(p, iec104fd))
+ {
+ iec104fd->reset();
+ }
+}
+
+//-------------------------------------------------------------------------
+// plugin stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{
+ return new Iec104Module;
+}
+
+static void mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static void iec104_init()
+{
+ Iec104FlowData::init();
+}
+
+static Inspector* iec104_ctor(Module*)
+{
+ return new Iec104;
+}
+
+static void iec104_dtor(Inspector* p)
+{
+ delete p;
+}
+
+//-------------------------------------------------------------------------
+
+static const InspectApi iec104_api =
+{
+ {
+ PT_INSPECTOR,
+ sizeof(InspectApi),
+ INSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ IEC104_NAME,
+ IEC104_HELP,
+ mod_ctor,
+ mod_dtor
+ },
+ IT_SERVICE,
+ PROTO_BIT__PDU,
+ nullptr,
+ "iec104",
+ iec104_init,
+ nullptr,
+ nullptr, // tinit
+ nullptr, // tterm
+ iec104_ctor,
+ iec104_dtor,
+ nullptr, // ssn
+ nullptr // reset
+};
+
+// BaseApi for each rule option
+extern const BaseApi* ips_iec104_asdu_func;
+extern const BaseApi* ips_iec104_apci_type;
+
+#ifdef BUILDING_SO
+SO_PUBLIC const BaseApi* snort_plugins[] =
+#else
+const BaseApi* sin_iec104[] =
+#endif
+{
+ &iec104_api.base,
+ ips_iec104_asdu_func,
+ ips_iec104_apci_type,
+ nullptr
+};
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104.h author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus.h (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm.h (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifndef IEC104_H
+#define IEC104_H
+
+#include "flow/flow.h"
+#include "framework/counts.h"
+
+#define IEC104_MIN_APCI_LEN 4 // Enough for the four control octets that are in every message
+
+struct Iec104Stats
+{
+ PegCount sessions;
+ PegCount frames;
+ PegCount concurrent_sessions;
+ PegCount max_concurrent_sessions;
+};
+
+struct Iec104SessionData
+{
+ uint8_t iec104_apci_type = -1;
+ uint8_t iec104_asdu_func = 0;
+
+ void session_data_reset()
+ {
+ iec104_apci_type = -1;
+ iec104_asdu_func = 0;
+ }
+};
+
+class Iec104FlowData: public snort::FlowData
+{
+public:
+ Iec104FlowData();
+ ~Iec104FlowData() override;
+
+ static void init();
+
+ void reset()
+ {
+ ssn_data.session_data_reset();
+ }
+
+ size_t size_of() override
+ {
+ return sizeof(*this);
+ }
+
+public:
+ static unsigned inspector_id;
+ Iec104SessionData ssn_data;
+};
+
+uint32_t get_message_type(uint32_t version, const char* name);
+uint32_t get_info_type(uint32_t version, const char* name);
+
+extern THREAD_LOCAL Iec104Stats iec104_stats;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_decode.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus_decode.cc (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm_decode.cc (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iec104_decode.h"
+
+#include "detection/detection_engine.h"
+#include "events/event_queue.h"
+#include "log/messages.h"
+#include "main/snort_debug.h"
+#include "protocols/packet.h"
+
+#include "iec104.h"
+#include "iec104_module.h"
+#include "iec104_parse_apdu.h"
+#include "iec104_trace.h"
+
+using namespace snort;
+
+//-------------------------------------------------------------------------
+// APCI determination
+//-------------------------------------------------------------------------
+
+// Function to determine the APCI type of the current message based on the
+// major and (where needed) minor codes
+// returns an ApciType enum value corresponding to the determined APCI
+static uint32_t getApciType(Packet* p)
+{
+ // overlay a generic apci struct over the first three bytes of the stream
+ const Iec104GenericApci* apci = (const Iec104GenericApci*) p->data;
+
+ // default apci type to a non-used value
+ // if this somehow makes it through it errors out in the apci type switch
+ uint32_t curApciType = IEC104_NO_APCI;
+
+ // Check the APCI Major type flag
+ if (apci->apciTypeMajor)
+ {
+ // Check the APCI Minor type flag
+ if (apci->apciTypeMinor)
+ {
+ // APCI Type U
+ curApciType = IEC104_APCI_TYPE_U;
+ }
+ else
+ {
+ // APCI Type S
+ curApciType = IEC104_APCI_TYPE_S;
+ }
+ }
+ else
+ {
+ // APCI Type I
+ curApciType = IEC104_APCI_TYPE_I;
+ }
+
+ return curApciType;
+}
+
+bool Iec104Decode(Packet* p, Iec104FlowData* iec104fd)
+{
+ if (p->dsize < IEC104_MIN_LEN)
+ {
+ return false;
+ }
+
+ // build the correct APCI based on the returned type
+ uint32_t apciType = getApciType(p);
+
+ if (apciType > 2)
+ {
+ // An APCI type cannot be determined. Message does not appear to be IEC104
+ return false;
+ }
+ else
+ {
+ // apply the appropriate structure to the packet buffer based on the
+ // earlier type determination
+ switch (apciType)
+ {
+ case IEC104_APCI_TYPE_U:
+ {
+ // build up the APCI
+ const Iec104ApciU* apci = (const Iec104ApciU*) p->data;
+
+ // set the apci type
+ iec104fd->ssn_data.iec104_apci_type = apciType;
+
+ // clear out the asdu since it isn't applicable here
+ iec104fd->ssn_data.iec104_asdu_func = IEC104_NO_ASDU;
+
+ // print out the APCI
+ print_debug_information(p, "Unnumbered Control Function APCI\n");
+ parseIec104ApciU(apci);
+
+ break;
+ }
+
+ case IEC104_APCI_TYPE_S:
+ {
+ // build up the APCI
+ const Iec104ApciS* apci = (const Iec104ApciS*) p->data;
+
+ // set the apci type
+ iec104fd->ssn_data.iec104_apci_type = apciType;
+
+ // clear out the asdu since it isn't applicable here
+ iec104fd->ssn_data.iec104_asdu_func = IEC104_NO_ASDU;
+
+ // print out the APCI
+ print_debug_information(p, "Numbered Supervisory Function APCI\n");
+ parseIec104ApciS(apci);
+
+ break;
+ }
+
+ case IEC104_APCI_TYPE_I:
+ {
+ // build up the APCI
+ const Iec104ApciI* apci = (const Iec104ApciI*) p->data;
+
+ // set the apci type
+ iec104fd->ssn_data.iec104_apci_type = apciType;
+
+ // set the asdu function type in the session data
+ iec104fd->ssn_data.iec104_asdu_func = apci->asdu.typeId;
+
+ // print out the APCI
+ print_debug_information(p, "Information Transfer Format APCI\n");
+ parseIec104ApciI(apci);
+
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+//
+// iec104_decode.h author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus_decode.h (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm_decode.h (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifndef IEC104_DECODE_H
+#define IEC104_DECODE_H
+
+namespace snort
+{
+struct Packet;
+}
+
+class Iec104FlowData;
+
+/* IEC104 defines */
+#define IEC104_START_ID 0x68
+
+/* Need 6 bytes for Start, Length, and 4 control field octets */
+#define IEC104_MIN_LEN 6
+
+bool Iec104Decode(snort::Packet*, Iec104FlowData* iec104fd);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_module.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus_module.c (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm_module.c (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iec104_module.h"
+
+#include "profiler/profiler.h"
+#include "trace/trace.h"
+
+#include "iec104.h"
+#include "iec104_trace.h"
+
+using namespace snort;
+
+THREAD_LOCAL ProfileStats iec104_prof;
+
+THREAD_LOCAL const Trace* iec104_trace = nullptr;
+
+static const TraceOption iec104_trace_options[] =
+{
+ { "identification", TRACE_IEC104_IDENTIFICATION, "enable IEC104 APDU identification trace logging" },
+
+ { nullptr, 0, nullptr }
+};
+
+//-------------------------------------------------------------------------
+// stats
+//-------------------------------------------------------------------------
+
+const PegInfo peg_names[] =
+{
+ { CountType::SUM, "sessions", "total sessions processed" },
+ { CountType::SUM, "frames", "total IEC104 messages" },
+ { CountType::NOW, "concurrent_sessions", "total concurrent IEC104 sessions" },
+ { CountType::MAX, "max_concurrent_sessions", "maximum concurrent IEC104 sessions" },
+
+ { CountType::END, nullptr, nullptr }
+};
+
+const PegInfo* Iec104Module::get_pegs() const
+{
+ return peg_names;
+}
+
+PegCount* Iec104Module::get_counts() const
+{
+ return (PegCount*) &iec104_stats;
+}
+
+//-------------------------------------------------------------------------
+// rules
+//-------------------------------------------------------------------------
+
+static const RuleMap Iec104_rules[] =
+{
+ { IEC104_BAD_LENGTH, IEC104_BAD_LENGTH_STR },
+ { IEC104_BAD_START, IEC104_BAD_START_STR },
+ { IEC104_RESERVED_ASDU_TYPE, IEC104_RESERVED_ASDU_TYPE_STR },
+ { IEC104_APCIU_RESERVED_FIELD_IN_USE, IEC104_APCIU_RESERVED_FIELD_IN_USE_STR },
+ { IEC104_APCIU_INVALID_MESSAGE_TYPE, IEC104_APCIU_INVALID_MESSAGE_TYPE_STR },
+ { IEC104_APCIS_RESERVED_FIELD_IN_USE, IEC104_APCIS_RESERVED_FIELD_IN_USE_STR },
+ { IEC104_APCII_NUM_ELEMENTS_SET_TO_ZERO, IEC104_APCII_NUM_ELEMENTS_SET_TO_ZERO_STR },
+ { IEC104_APCII_INVALID_SQ_VALUE, IEC104_APCII_INVALID_SQ_VALUE_STR },
+ { IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE, IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE_STR },
+ { IEC104_RESERVED_COI, IEC104_RESERVED_COI_STR },
+ { IEC104_RESERVED_QOI, IEC104_RESERVED_QOI_STR },
+ { IEC104_RESERVED_QCC, IEC104_RESERVED_QCC_STR },
+ { IEC104_RESERVED_QPM_KPA, IEC104_RESERVED_QPM_KPA_STR },
+ { IEC104_ABNORMAL_QPM_LPC, IEC104_ABNORMAL_QPM_LPC_STR },
+ { IEC104_ABNORMAL_QPM_POP, IEC104_ABNORMAL_QPM_POP_STR },
+ { IEC104_RESERVED_QPA, IEC104_RESERVED_QPA_STR },
+ { IEC104_RESERVED_QOC, IEC104_RESERVED_QOC_STR },
+ { IEC104_RESERVED_QRP, IEC104_RESERVED_QRP_STR },
+ { IEC104_RESERVED_FRQ, IEC104_RESERVED_FRQ_STR },
+ { IEC104_RESERVED_SRQ, IEC104_RESERVED_SRQ_STR },
+ { IEC104_RESERVED_SCQ, IEC104_RESERVED_SCQ_STR },
+ { IEC104_RESERVED_LSQ, IEC104_RESERVED_LSQ_STR },
+ { IEC104_RESERVED_AFQ, IEC104_RESERVED_AFQ_STR },
+ { IEC104_VSQ_ABNORMAL_SQ, IEC104_VSQ_ABNORMAL_SQ_STR },
+ { IEC104_RESERVED_CAUSE_TX, IEC104_RESERVED_CAUSE_TX_STR },
+ { IEC104_INVALID_CAUSE_TX, IEC104_INVALID_CAUSE_TX_STR },
+ { IEC104_INVALID_COMMON_ADDRESS, IEC104_INVALID_COMMON_ADDRESS_STR },
+ { IEC104_RESERVED_SIQ, IEC104_RESERVED_SIQ_STR },
+ { IEC104_RESERVED_DIQ, IEC104_RESERVED_DIQ_STR },
+ { IEC104_RESERVED_QDS, IEC104_RESERVED_QDS_STR },
+ { IEC104_RESERVED_QDP, IEC104_RESERVED_QDP_STR },
+ { IEC104_RESERVED_IEEE_STD_754_NAN, IEC104_RESERVED_IEEE_STD_754_NAN_STR },
+ { IEC104_RESERVED_IEEE_STD_754_INFINITY, IEC104_RESERVED_IEEE_STD_754_INFINITY_STR },
+ { IEC104_RESERVED_SEP, IEC104_RESERVED_SEP_STR },
+ { IEC104_RESERVED_SPE, IEC104_RESERVED_SPE_STR },
+ { IEC104_RESERVED_OCI, IEC104_RESERVED_OCI_STR },
+ { IEC104_INVALID_FBP, IEC104_INVALID_FBP_STR },
+ { IEC104_RESERVED_SCO, IEC104_RESERVED_SCO_STR },
+ { IEC104_INVALID_DCO, IEC104_INVALID_DCO_STR },
+ { IEC104_RESERVED_RCO, IEC104_RESERVED_RCO_STR },
+ { IEC104_INVALID_MS_IN_MINUTE, IEC104_INVALID_MS_IN_MINUTE_STR },
+ { IEC104_INVALID_MINS_IN_HOUR, IEC104_INVALID_MINS_IN_HOUR_STR },
+ { IEC104_RESERVED_MINS_IN_HOUR, IEC104_RESERVED_MINS_IN_HOUR_STR },
+ { IEC104_INVALID_HOURS_IN_DAY, IEC104_INVALID_HOURS_IN_DAY_STR },
+ { IEC104_RESERVED_HOURS_IN_DAY, IEC104_RESERVED_HOURS_IN_DAY_STR },
+ { IEC104_INVALID_DAY_OF_MONTH, IEC104_INVALID_DAY_OF_MONTH_STR },
+ { IEC104_INVALID_MONTH, IEC104_INVALID_MONTH_STR },
+ { IEC104_RESERVED_MONTH, IEC104_RESERVED_MONTH_STR },
+ { IEC104_INVALID_YEAR, IEC104_INVALID_YEAR_STR },
+ { IEC104_NULL_LOS_VALUE, IEC104_NULL_LOS_VALUE_STR },
+ { IEC104_INVALID_LOS_VALUE, IEC104_INVALID_LOS_VALUE_STR },
+ { IEC104_RESERVED_YEAR, IEC104_RESERVED_YEAR_STR },
+ { IEC104_RESERVED_SOF, IEC104_RESERVED_SOF_STR },
+ { IEC104_RESERVED_QOS, IEC104_RESERVED_QOS_STR },
+
+ { 0, nullptr }
+};
+
+void Iec104Module::set_trace(const Trace* trace) const
+{ iec104_trace = trace; }
+
+const TraceOption* Iec104Module::get_trace_options() const
+{ return iec104_trace_options; }
+
+const RuleMap* Iec104Module::get_rules() const
+{
+ return Iec104_rules;
+}
+
+//-------------------------------------------------------------------------
+// params
+//-------------------------------------------------------------------------
+
+Iec104Module::Iec104Module() :
+ Module(IEC104_NAME, IEC104_HELP)
+{
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_module.h author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus_module.h (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm_module.h (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifndef IEC104_MODULE_H
+#define IEC104_MODULE_H
+
+#include "framework/module.h"
+
+#define GID_IEC104 151
+
+#define IEC104_NAME "iec104"
+#define IEC104_HELP "iec104 inspection"
+
+extern THREAD_LOCAL snort::ProfileStats iec104_prof;
+
+class Iec104Module: public snort::Module
+{
+public:
+ Iec104Module();
+
+ unsigned get_gid() const override
+ {
+ return GID_IEC104;
+ }
+
+ const snort::RuleMap* get_rules() const override;
+
+ const PegInfo* get_pegs() const override;
+ PegCount* get_counts() const override;
+
+ snort::ProfileStats* get_profile() const override
+ {
+ return &iec104_prof;
+ }
+
+ Usage get_usage() const override
+ {
+ return INSPECT;
+ }
+
+ bool is_bindable() const override
+ {
+ return true;
+ }
+
+ void set_trace(const snort::Trace*) const override;
+ const snort::TraceOption* get_trace_options() const override;
+};
+
+#endif
+
+#ifndef IEC104_RULES
+#define IEC104_RULES
+
+#define IEC104_BAD_LENGTH 1
+#define IEC104_BAD_START 2
+#define IEC104_RESERVED_ASDU_TYPE 3
+#define IEC104_APCIU_RESERVED_FIELD_IN_USE 4
+#define IEC104_APCIU_INVALID_MESSAGE_TYPE 5
+#define IEC104_APCIS_RESERVED_FIELD_IN_USE 6
+#define IEC104_APCII_NUM_ELEMENTS_SET_TO_ZERO 7
+#define IEC104_APCII_INVALID_SQ_VALUE 8
+#define IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE 9
+#define IEC104_RESERVED_COI 10
+#define IEC104_RESERVED_QOI 11
+#define IEC104_RESERVED_QCC 12
+#define IEC104_RESERVED_QPM_KPA 13
+#define IEC104_ABNORMAL_QPM_LPC 14
+#define IEC104_ABNORMAL_QPM_POP 15
+#define IEC104_RESERVED_QPA 16
+#define IEC104_RESERVED_QOC 17
+#define IEC104_RESERVED_QRP 18
+#define IEC104_RESERVED_FRQ 19
+#define IEC104_RESERVED_SRQ 20
+#define IEC104_RESERVED_SCQ 21
+#define IEC104_RESERVED_LSQ 22
+#define IEC104_RESERVED_AFQ 23
+#define IEC104_VSQ_ABNORMAL_SQ 24
+#define IEC104_RESERVED_SIQ 25
+#define IEC104_RESERVED_DIQ 26
+#define IEC104_RESERVED_CAUSE_TX 27
+#define IEC104_INVALID_CAUSE_TX 28
+#define IEC104_INVALID_COMMON_ADDRESS 29
+#define IEC104_RESERVED_QDS 30
+#define IEC104_RESERVED_QDP 31
+#define IEC104_RESERVED_IEEE_STD_754_NAN 32
+#define IEC104_RESERVED_IEEE_STD_754_INFINITY 33
+#define IEC104_RESERVED_SEP 34
+#define IEC104_RESERVED_SPE 35
+#define IEC104_RESERVED_OCI 36
+#define IEC104_INVALID_FBP 37
+#define IEC104_RESERVED_SCO 38
+#define IEC104_INVALID_DCO 39
+#define IEC104_RESERVED_RCO 40
+#define IEC104_INVALID_MS_IN_MINUTE 41
+#define IEC104_INVALID_MINS_IN_HOUR 42
+#define IEC104_RESERVED_MINS_IN_HOUR 43
+#define IEC104_INVALID_HOURS_IN_DAY 44
+#define IEC104_RESERVED_HOURS_IN_DAY 45
+#define IEC104_INVALID_DAY_OF_MONTH 46
+#define IEC104_INVALID_MONTH 47
+#define IEC104_RESERVED_MONTH 48
+#define IEC104_INVALID_YEAR 49
+#define IEC104_RESERVED_YEAR 50
+#define IEC104_NULL_LOS_VALUE 51
+#define IEC104_INVALID_LOS_VALUE 52
+#define IEC104_RESERVED_SOF 53
+#define IEC104_RESERVED_QOS 54
+
+#define IEC104_BAD_LENGTH_STR "(spp_iec104): Length in IEC104 APCI header does not match the length needed for the given IEC104 ASDU type id."
+#define IEC104_BAD_START_STR "(spp_iec104): IEC104 Start byte does not match 0x68."
+#define IEC104_RESERVED_ASDU_TYPE_STR "(spp_iec104): Reserved IEC104 ASDU type id in use."
+#define IEC104_APCIU_RESERVED_FIELD_IN_USE_STR "(spp_iec104): IEC104 APCI U Reserved field contains a non-default value."
+#define IEC104_APCIU_INVALID_MESSAGE_TYPE_STR "(spp_iec104): IEC104 APCI U message type was set to an invalid value."
+#define IEC104_APCIS_RESERVED_FIELD_IN_USE_STR "(spp_iec104): IEC104 APCI S Reserved field contains a non-default value."
+#define IEC104_APCII_NUM_ELEMENTS_SET_TO_ZERO_STR "(spp_iec104): IEC104 APCI I number of elements set to zero."
+#define IEC104_APCII_INVALID_SQ_VALUE_STR "(spp_iec104): IEC104 APCI I SQ bit set on an ASDU that does not support the feature."
+#define IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE_STR "(spp_iec104): IEC104 APCI I number of elements set to greater than one on an ASDU that does not support the feature."
+#define IEC104_RESERVED_COI_STR "(spp_iec104): IEC104 APCI I Cause of Initialization set to a reserved value."
+#define IEC104_RESERVED_QOI_STR "(spp_iec104): IEC104 APCI I Qualifier of Interrogation Command set to a reserved value."
+#define IEC104_RESERVED_QCC_STR "(spp_iec104): IEC104 APCI I Qualifier of Counter Interrogation Command request parameter set to a reserved value."
+#define IEC104_RESERVED_QPM_KPA_STR "(spp_iec104): IEC104 APCI I Qualifier of Parameter of Measured Values kind of parameter set to a reserved value."
+#define IEC104_ABNORMAL_QPM_LPC_STR "(spp_iec104): IEC104 APCI I Qualifier of Parameter of Measured Values local parameter change set to a technically valid but unused value."
+#define IEC104_ABNORMAL_QPM_POP_STR "(spp_iec104): IEC104 APCI I Qualifier of Parameter of Measured Values parameter option set to a technically valid but unused value."
+#define IEC104_RESERVED_QPA_STR "(spp_iec104): IEC104 APCI I Qualifier of Parameter Activation set to a reserved value."
+#define IEC104_RESERVED_QOC_STR "(spp_iec104): IEC104 APCI I Qualifier of Command set to a reserved value."
+#define IEC104_RESERVED_QRP_STR "(spp_iec104): IEC104 APCI I Qualifier of Reset Process set to a reserved value."
+#define IEC104_RESERVED_FRQ_STR "(spp_iec104): IEC104 APCI I File Ready Qualifier set to a reserved value."
+#define IEC104_RESERVED_SRQ_STR "(spp_iec104): IEC104 APCI I Section Ready Qualifier set to a reserved value."
+#define IEC104_RESERVED_SCQ_STR "(spp_iec104): IEC104 APCI I Select and Call Qualifier set to a reserved value."
+#define IEC104_RESERVED_LSQ_STR "(spp_iec104): IEC104 APCI I Last Section or Segment Qualifier set to a reserved value."
+#define IEC104_RESERVED_AFQ_STR "(spp_iec104): IEC104 APCI I Acknowledge File or Section Qualifier set to a reserved value."
+#define IEC104_VSQ_ABNORMAL_SQ_STR "(spp_iec104): IEC104 APCI I Structure Qualifier set on a message where it should have no effect."
+#define IEC104_RESERVED_CAUSE_TX_STR "(spp_iec104): IEC104 APCI I Cause of Transmission set to a reserved value."
+#define IEC104_INVALID_CAUSE_TX_STR "(spp_iec104): IEC104 APCI I Cause of Transmission set to a value not allowed for the ASDU."
+#define IEC104_INVALID_COMMON_ADDRESS_STR "(spp_iec104): IEC104 APCI I invalid two octet common address value detected."
+#define IEC104_RESERVED_SIQ_STR "(spp_iec104): IEC104 APCI I Single Point Information Reserved field contains a non-default value."
+#define IEC104_RESERVED_DIQ_STR "(spp_iec104): IEC104 APCI I Double Point Information Reserved field contains a non-default value."
+#define IEC104_RESERVED_QDS_STR "(spp_iec104): IEC104 APCI I Quality Descriptor Structure Reserved field contains a non-default value."
+#define IEC104_RESERVED_QDP_STR "(spp_iec104): IEC104 APCI I Quality Descriptor for Events of Protection Equipment Structure Reserved field contains a non-default value."
+#define IEC104_RESERVED_IEEE_STD_754_NAN_STR "(spp_iec104): IEC104 APCI I IEEE STD 754 value results in NaN."
+#define IEC104_RESERVED_IEEE_STD_754_INFINITY_STR "(spp_iec104): IEC104 APCI I IEEE STD 754 value results in infinity."
+#define IEC104_RESERVED_SEP_STR "(spp_iec104): IEC104 APCI I Single Event of Protection Equipment Structure Reserved field contains a non-default value."
+#define IEC104_RESERVED_SPE_STR "(spp_iec104): IEC104 APCI I Start Event of Protection Equipment Structure Reserved field contains a non-default value."
+#define IEC104_RESERVED_OCI_STR "(spp_iec104): IEC104 APCI I Output Circuit Information Structure Reserved field contains a non-default value."
+#define IEC104_INVALID_FBP_STR "(spp_iec104): IEC104 APCI I Abnormal Fixed Test Bit Pattern detected."
+#define IEC104_RESERVED_SCO_STR "(spp_iec104): IEC104 APCI I Single Command Structure Reserved field contains a non-default value."
+#define IEC104_INVALID_DCO_STR "(spp_iec104): IEC104 APCI I Double Command Structure contains an invalid value."
+#define IEC104_RESERVED_RCO_STR "(spp_iec104): IEC104 APCI I Regulating Step Command Structure Reserved field contains a non-default value."
+#define IEC104_INVALID_MS_IN_MINUTE_STR "(spp_iec104): IEC104 APCI I Time2a Millisecond set outside of the allowable range."
+#define IEC104_INVALID_MINS_IN_HOUR_STR "(spp_iec104): IEC104 APCI I Time2a Minute set outside of the allowable range."
+#define IEC104_RESERVED_MINS_IN_HOUR_STR "(spp_iec104): IEC104 APCI I Time2a Minute Reserved field contains a non-default value."
+#define IEC104_INVALID_HOURS_IN_DAY_STR "(spp_iec104): IEC104 APCI I Time2a Hours set outside of the allowable range."
+#define IEC104_RESERVED_HOURS_IN_DAY_STR "(spp_iec104): IEC104 APCI I Time2a Hours Reserved field contains a non-default value."
+#define IEC104_INVALID_DAY_OF_MONTH_STR "(spp_iec104): IEC104 APCI I Time2a Day of Month set outside of the allowable range."
+#define IEC104_INVALID_MONTH_STR "(spp_iec104): IEC104 APCI I Time2a Month set outside of the allowable range."
+#define IEC104_RESERVED_MONTH_STR "(spp_iec104): IEC104 APCI I Time2a Month Reserved field contains a non-default value."
+#define IEC104_INVALID_YEAR_STR "(spp_iec104): IEC104 APCI I Time2a Year set outside of the allowable range."
+#define IEC104_NULL_LOS_VALUE_STR "(spp_iec104): IEC104 APCI I a null Length of Segment value has been detected."
+#define IEC104_INVALID_LOS_VALUE_STR "(spp_iec104): IEC104 APCI I an invalid Length of Segment value has been detected."
+#define IEC104_RESERVED_YEAR_STR "(spp_iec104): IEC104 APCI I Time2a Year Reserved field contains a non-default value."
+#define IEC104_RESERVED_SOF_STR "(spp_iec104): IEC104 APCI I Status of File set to a reserved value."
+#define IEC104_RESERVED_QOS_STR "(spp_iec104): IEC104 APCI I Qualifier of Set Point Command ql field set to a reserved value."
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_paf.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus_paf.cc (author Ryan Jordan)
+// modeled after s7comm_paf.cc (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+// Protocol-Aware Flushing (PAF) code for the IEC104 preprocessor.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iec104_paf.h"
+
+#include "detection/detection_engine.h"
+#include "events/event_queue.h"
+#include "profiler/profiler.h"
+
+#include "iec104.h"
+#include "iec104_decode.h"
+#include "iec104_module.h"
+
+using namespace snort;
+
+#define IEC104_MIN_HDR_LEN 2 // Enough for the Start and Length fields
+
+Iec104Splitter::Iec104Splitter(bool b) :
+ StreamSplitter(b)
+{
+ state = IEC104_PAF_STATE__START;
+ iec104_apci_length = 0;
+}
+
+// IEC104/TCP PAF:
+// Statefully inspects IEC104 traffic from the start of a session,
+// Reads up until the length octet is found, then sets a flush point.
+
+StreamSplitter::Status Iec104Splitter::scan(Packet*, const uint8_t* data, uint32_t len,
+ uint32_t /*flags*/, uint32_t* fp)
+{
+ Profile profile(iec104_prof);
+
+ uint32_t bytes_processed = 0;
+
+ /* Process this packet 1 byte at a time */
+ while (bytes_processed < len)
+ {
+ switch (state)
+ {
+ // skip the start state
+ case IEC104_PAF_STATE__START:
+ {
+ state = IEC104_PAF_STATE__LEN;
+ break;
+ }
+
+ // length field is only one byte long
+ case IEC104_PAF_STATE__LEN:
+ {
+ iec104_apci_length = *(data + bytes_processed);
+ state = IEC104_PAF_STATE__SET_FLUSH;
+ break;
+ }
+
+ case IEC104_PAF_STATE__SET_FLUSH:
+ {
+ *fp = iec104_apci_length + bytes_processed; // flush point at the end of payload
+ state = IEC104_PAF_STATE__START;
+ iec104_apci_length = 0;
+ return StreamSplitter::FLUSH;
+ }
+ }
+ bytes_processed++;
+ }
+
+ return StreamSplitter::SEARCH;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_paf.h author Jared Rittle <jared.rittle@cisco.com>
+// modeled after modbus_paf.h (author Russ Combs <rucombs@cisco.com>)
+// modeled after s7comm_paf.h (author Pradeep Damodharan <prdamodh@cisco.com>)
+
+#ifndef IEC104_PAF__H
+#define IEC104_PAF__H
+
+// Protocol-Aware Flushing (PAF) code for the IEC104 preprocessor.
+
+#include "stream/stream_splitter.h"
+
+enum iec104_paf_state_t
+{
+ IEC104_PAF_STATE__START = 0,
+ IEC104_PAF_STATE__LEN = 1,
+ IEC104_PAF_STATE__SET_FLUSH = 2,
+};
+
+class Iec104Splitter: public snort::StreamSplitter
+{
+public:
+ Iec104Splitter(bool);
+
+ Status scan(snort::Packet*, const uint8_t* data, uint32_t len, uint32_t flags,
+ uint32_t* fp) override;
+
+ bool is_paf() override { return true; }
+
+private:
+ iec104_paf_state_t state;
+ uint16_t iec104_apci_length;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_parse_apdu.cc author Jared Rittle <jared.rittle@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iec104_parse_apdu.h"
+
+#include "detection/detection_engine.h"
+#include "events/event_queue.h"
+#include "protocols/packet.h"
+
+#include "iec104.h"
+#include "iec104_decode.h"
+#include "iec104_module.h"
+#include "iec104_parse_information_object_elements.h"
+
+using namespace snort;
+
+// perform some checks on the ASDU
+static bool checkIec104Asdu(Iec104AsduCheck curAsduCheck)
+{
+ // keep a flag to indicate whether we should exit after executing
+ // taking this approach instead of returning directly as multiple of these
+ // cases could exist and we want to alert on all of them
+ bool continueProcessing = true;
+
+ // make sure the number of elements is not zero
+ if (curAsduCheck.apci->asdu.variableStructureQualifier.numberOfElements == 0)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCII_NUM_ELEMENTS_SET_TO_ZERO);
+
+ // indicate that we should stop parsing after the return of this function
+ continueProcessing = false;
+ }
+
+ // When indicated, the ASDU should not have a SQ value of 0
+ // this is not the case for most asdus
+ if (!curAsduCheck.sq0Allowed)
+ {
+ if (curAsduCheck.apci->asdu.variableStructureQualifier.sq != IEC104_SQ_TRUE)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCII_INVALID_SQ_VALUE);
+
+ // indicate that we should stop parsing after the return of this function
+ continueProcessing = false;
+ }
+ }
+
+ // When indicated, the ASDU should not have a SQ value of 1
+ // this is not the case for most asdus
+ if (!curAsduCheck.sq1Allowed)
+ {
+ if (curAsduCheck.apci->asdu.variableStructureQualifier.sq != IEC104_SQ_FALSE)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCII_INVALID_SQ_VALUE);
+
+ // indicate that we should stop parsing after the return of this function
+ continueProcessing = false;
+ }
+ }
+
+ // When indicated, the ASDU should not have a number of items greater than 1
+ // this is not the case for most asdus
+ if (!curAsduCheck.multipleIOAllowed)
+ {
+ if (curAsduCheck.apci->asdu.variableStructureQualifier.numberOfElements > 1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE);
+
+ // indicate that we should stop parsing after the return of this function
+ continueProcessing = false;
+ }
+ }
+
+ // Verify that the cause of transmission indicated by the sender is one that is
+ // allowed for the message type
+ switch (curAsduCheck.apci->asdu.causeOfTransmission.causeOfTransmission)
+ {
+ case IEC104_CAUSE_TX_PER_CYC:
+ {
+ if (!curAsduCheck.checkCauseOfTx.percyc)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_BACK:
+ {
+ if (!curAsduCheck.checkCauseOfTx.back)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_SPONT:
+ {
+ if (!curAsduCheck.checkCauseOfTx.spont)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INIT:
+ {
+ if (!curAsduCheck.checkCauseOfTx.init)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_REQ:
+ {
+ if (!curAsduCheck.checkCauseOfTx.req)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_ACT:
+ {
+ if (!curAsduCheck.checkCauseOfTx.act)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_ACTCON:
+ {
+ if (!curAsduCheck.checkCauseOfTx.actcon)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_DEACT:
+ {
+ if (!curAsduCheck.checkCauseOfTx.deact)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_DEACTCON:
+ {
+ if (!curAsduCheck.checkCauseOfTx.deactcon)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_ACTTERM:
+ {
+ if (!curAsduCheck.checkCauseOfTx.actterm)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_RETREM:
+ {
+ if (!curAsduCheck.checkCauseOfTx.retrem)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_RETLOC:
+ {
+ if (!curAsduCheck.checkCauseOfTx.retloc)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_FILE:
+ {
+ if (!curAsduCheck.checkCauseOfTx.file)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INROGEN:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inrogen)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO1:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO2:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro2)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO3:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro3)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO4:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro4)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO5:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro5)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO6:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro6)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO7:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro7)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO8:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro8)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO9:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro9)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO10:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro10)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO11:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro11)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO12:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro12)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO13:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro13)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO14:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro14)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO15:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro15)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_INRO16:
+ {
+ if (!curAsduCheck.checkCauseOfTx.inro16)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_REQCOGEN:
+ {
+ if (!curAsduCheck.checkCauseOfTx.reqcogen)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_REQCO1:
+ {
+ if (!curAsduCheck.checkCauseOfTx.reqco1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_REQCO2:
+ {
+ if (!curAsduCheck.checkCauseOfTx.reqco2)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_REQCO3:
+ {
+ if (!curAsduCheck.checkCauseOfTx.reqco3)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_REQCO4:
+ {
+ if (!curAsduCheck.checkCauseOfTx.reqco4)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_UNKNOWN_TYPE_ID:
+ {
+ if (!curAsduCheck.checkCauseOfTx.unk_type_id)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_UNKNOWN_CAUSE_OF_TX:
+ {
+ if (!curAsduCheck.checkCauseOfTx.unk_cause_tx)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_UNKNOWN_COMMON_ADDR_OF_ASDU:
+ {
+ if (!curAsduCheck.checkCauseOfTx.unk_common_addr)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_UNKNOWN_IOA:
+ {
+ if (!curAsduCheck.checkCauseOfTx.unk_info_addr)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_CAUSE_TX);
+ }
+ break;
+ }
+
+ case IEC104_CAUSE_TX_RES14: // 14-19 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES15: // 14-19 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES16: // 14-19 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES17: // 14-19 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES18: // 14-19 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES19: // 14-19 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES42: // 42-43 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES43: // 42-43 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES48: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES49: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES50: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES51: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES52: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES53: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES54: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES55: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES56: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES57: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES58: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES59: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES60: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES61: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES62: // 48-63 reserved. falls through into other reserved processing
+ case IEC104_CAUSE_TX_RES63: // 48-63 reserved
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_CAUSE_TX);
+ break;
+ }
+
+ default:
+ {
+ // Invalid Cause of Transmission
+ }
+ }
+
+ return continueProcessing;
+}
+
+// Function to perform the desired parsing based off of the ASDU type
+// This should not be called directly by anything other than parseGenericAsdu
+static void parseIec104GenericIOGroup(const GenericIec104AsduIOGroup* genericIOGroup)
+{
+ // determine which ASDU parsing logic to run based off of the passed type
+ switch (genericIOGroup->asduType)
+ {
+ case IEC104_ASDU_M_SP_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_sp_na_1IOGroup->ioa);
+ }
+ parseIec104Siq(&genericIOGroup->m_sp_na_1IOSubgroup->siq);
+ break;
+ }
+
+ case IEC104_ASDU_M_SP_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_sp_ta_1IOGroup->ioa);
+ }
+ parseIec104Siq(&genericIOGroup->m_sp_ta_1IOSubgroup->siq);
+ parseIec104Cp24Time2a(&genericIOGroup->m_sp_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_dp_na_1IOGroup->ioa);
+ }
+ parseIec104Diq(&genericIOGroup->m_dp_na_1IOSubgroup->diq);
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_dp_ta_1IOGroup->ioa);
+ }
+ parseIec104Diq(&genericIOGroup->m_dp_ta_1IOSubgroup->diq);
+ parseIec104Cp24Time2a(&genericIOGroup->m_dp_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_st_na_1IOGroup->ioa);
+ }
+ parseIec104Vti(&genericIOGroup->m_st_na_1IOSubgroup->vti);
+ parseIec104Qds(&genericIOGroup->m_st_na_1IOSubgroup->qds);
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_st_ta_1IOGroup->ioa);
+ }
+ parseIec104Vti(&genericIOGroup->m_st_ta_1IOSubgroup->vti);
+ parseIec104Qds(&genericIOGroup->m_st_ta_1IOSubgroup->qds);
+ parseIec104Cp24Time2a(&genericIOGroup->m_st_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_bo_na_1IOGroup->ioa);
+ }
+ parseIec104Bsi(&genericIOGroup->m_bo_na_1IOSubgroup->bsi);
+ parseIec104Qds(&genericIOGroup->m_bo_na_1IOSubgroup->qds);
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_bo_ta_1IOGroup->ioa);
+ }
+ parseIec104Bsi(&genericIOGroup->m_bo_ta_1IOSubgroup->bsi);
+ parseIec104Qds(&genericIOGroup->m_bo_ta_1IOSubgroup->qds);
+ parseIec104Cp24Time2a(&genericIOGroup->m_bo_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_na_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->m_me_na_1IOSubgroup->nva);
+ parseIec104Qds(&genericIOGroup->m_me_na_1IOSubgroup->qds);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_ta_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->m_me_ta_1IOSubgroup->nva);
+ parseIec104Qds(&genericIOGroup->m_me_ta_1IOSubgroup->qds);
+ parseIec104Cp24Time2a(&genericIOGroup->m_me_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_nb_1IOGroup->ioa);
+ }
+ parseIec104Sva(&genericIOGroup->m_me_nb_1IOSubgroup->sva);
+ parseIec104Qds(&genericIOGroup->m_me_nb_1IOSubgroup->qds);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_tb_1IOGroup->ioa);
+ }
+ parseIec104Sva(&genericIOGroup->m_me_tb_1IOSubgroup->sva);
+ parseIec104Qds(&genericIOGroup->m_me_tb_1IOSubgroup->qds);
+ parseIec104Cp24Time2a(&genericIOGroup->m_me_tb_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NC_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_nc_1IOGroup->ioa);
+ }
+ parseIec104IeeeStd754(&genericIOGroup->m_me_nc_1IOSubgroup->ieeeStd754);
+ parseIec104Qds(&genericIOGroup->m_me_nc_1IOSubgroup->qds);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TC_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_tc_1IOGroup->ioa);
+ }
+ parseIec104IeeeStd754(&genericIOGroup->m_me_tc_1IOSubgroup->ieeeStd754);
+ parseIec104Qds(&genericIOGroup->m_me_tc_1IOSubgroup->qds);
+ parseIec104Cp24Time2a(&genericIOGroup->m_me_tc_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_it_na_1IOGroup->ioa);
+ }
+ parseIec104Bcr(&genericIOGroup->m_it_na_1IOSubgroup->bcr);
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_it_ta_1IOGroup->ioa);
+ }
+ parseIec104Bcr(&genericIOGroup->m_it_ta_1IOSubgroup->bcr);
+ parseIec104Cp24Time2a(&genericIOGroup->m_it_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ep_ta_1IOGroup->ioa);
+ }
+ parseIec104Sep(&genericIOGroup->m_ep_ta_1IOSubgroup->sep);
+ parseIec104Cp16Time2a(&genericIOGroup->m_ep_ta_1IOSubgroup->elapsedTime);
+ parseIec104Cp24Time2a(&genericIOGroup->m_ep_ta_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ep_tb_1IOGroup->ioa);
+ }
+ parseIec104Spe(&genericIOGroup->m_ep_tb_1IOSubgroup->spe);
+ parseIec104Qdp(&genericIOGroup->m_ep_tb_1IOSubgroup->qdp);
+ parseIec104Cp16Time2a(&genericIOGroup->m_ep_tb_1IOSubgroup->relayDurationTime);
+ parseIec104Cp24Time2a(&genericIOGroup->m_ep_tb_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TC_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ep_tc_1IOGroup->ioa);
+ }
+ parseIec104Oci(&genericIOGroup->m_ep_tc_1IOSubgroup->oci);
+ parseIec104Qdp(&genericIOGroup->m_ep_tc_1IOSubgroup->qdp);
+ parseIec104Cp16Time2a(&genericIOGroup->m_ep_tc_1IOSubgroup->relayOperatingTime);
+ parseIec104Cp24Time2a(&genericIOGroup->m_ep_tc_1IOSubgroup->threeOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_PS_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ps_na_1IOGroup->ioa);
+ }
+ parseIec104Scd(&genericIOGroup->m_ps_na_1IOSubgroup->scd);
+ parseIec104Qds(&genericIOGroup->m_ps_na_1IOSubgroup->qds);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_ND_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_nd_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->m_me_nd_1IOSubgroup->nva);
+ break;
+ }
+
+ case IEC104_ASDU_M_SP_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_sp_tb_1IOGroup->ioa);
+ }
+ parseIec104Siq(&genericIOGroup->m_sp_tb_1IOSubgroup->siq);
+ parseIec104Cp56Time2a(&genericIOGroup->m_sp_tb_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_dp_tb_1IOGroup->ioa);
+ }
+ parseIec104Diq(&genericIOGroup->m_dp_tb_1IOSubgroup->diq);
+ parseIec104Cp56Time2a(&genericIOGroup->m_dp_tb_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_st_tb_1IOGroup->ioa);
+ }
+ parseIec104Vti(&genericIOGroup->m_st_tb_1IOSubgroup->vti);
+ parseIec104Qds(&genericIOGroup->m_st_tb_1IOSubgroup->qds);
+ parseIec104Cp56Time2a(&genericIOGroup->m_st_tb_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_bo_tb_1IOGroup->ioa);
+ }
+ parseIec104Bsi(&genericIOGroup->m_bo_tb_1IOSubgroup->bsi);
+ parseIec104Qds(&genericIOGroup->m_bo_tb_1IOSubgroup->qds);
+ parseIec104Cp56Time2a(&genericIOGroup->m_bo_tb_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TD_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_td_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->m_me_td_1IOSubgroup->nva);
+ parseIec104Qds(&genericIOGroup->m_me_td_1IOSubgroup->qds);
+ parseIec104Cp56Time2a(&genericIOGroup->m_me_td_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TE_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_te_1IOGroup->ioa);
+ }
+ parseIec104Sva(&genericIOGroup->m_me_te_1IOSubgroup->sva);
+ parseIec104Qds(&genericIOGroup->m_me_te_1IOSubgroup->qds);
+ parseIec104Cp56Time2a(&genericIOGroup->m_me_te_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TF_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_me_tf_1IOGroup->ioa);
+ }
+ parseIec104IeeeStd754(&genericIOGroup->m_me_tf_1IOSubgroup->ieeeStd754);
+ parseIec104Qds(&genericIOGroup->m_me_tf_1IOSubgroup->qds);
+ parseIec104Cp56Time2a(&genericIOGroup->m_me_tf_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_it_tb_1IOGroup->ioa);
+ }
+ parseIec104Bcr(&genericIOGroup->m_it_tb_1IOSubgroup->bcr);
+ parseIec104Cp56Time2a(&genericIOGroup->m_it_tb_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TD_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ep_td_1IOGroup->ioa);
+ }
+ parseIec104Sep(&genericIOGroup->m_ep_td_1IOSubgroup->sep);
+ parseIec104Cp16Time2a(&genericIOGroup->m_ep_td_1IOSubgroup->elapsedTime);
+ parseIec104Cp56Time2a(&genericIOGroup->m_ep_td_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TE_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ep_te_1IOGroup->ioa);
+ }
+ parseIec104Sep(&genericIOGroup->m_ep_te_1IOSubgroup->sep);
+ parseIec104Qdp(&genericIOGroup->m_ep_te_1IOSubgroup->qdp);
+ parseIec104Cp16Time2a(&genericIOGroup->m_ep_te_1IOSubgroup->relayDurationTime);
+ parseIec104Cp56Time2a(&genericIOGroup->m_ep_te_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TF_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ep_tf_1IOGroup->ioa);
+ }
+ parseIec104Oci(&genericIOGroup->m_ep_tf_1IOSubgroup->oci);
+ parseIec104Qdp(&genericIOGroup->m_ep_tf_1IOSubgroup->qdp);
+ parseIec104Cp16Time2a(&genericIOGroup->m_ep_tf_1IOSubgroup->relayDurationTime);
+ parseIec104Cp56Time2a(&genericIOGroup->m_ep_tf_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_SC_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_sc_na_1IOGroup->ioa);
+ }
+ parseIec104Sco(&genericIOGroup->c_sc_na_1IOSubgroup->sco);
+ break;
+ }
+
+ case IEC104_ASDU_C_DC_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_dc_na_1IOGroup->ioa);
+ }
+ parseIec104Dco(&genericIOGroup->c_dc_na_1IOSubgroup->dco);
+ break;
+ }
+
+ case IEC104_ASDU_C_RC_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_rc_na_1IOGroup->ioa);
+ }
+ parseIec104Rco(&genericIOGroup->c_rc_na_1IOSubgroup->rco);
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_se_na_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->c_se_na_1IOSubgroup->nva);
+ parseIec104Qos(&genericIOGroup->c_se_na_1IOSubgroup->qos);
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_se_nb_1IOGroup->ioa);
+ }
+ parseIec104Sva(&genericIOGroup->c_se_nb_1IOSubgroup->sva);
+ parseIec104Qos(&genericIOGroup->c_se_nb_1IOSubgroup->qos);
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NC_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_se_nc_1IOGroup->ioa);
+ }
+ parseIec104IeeeStd754(&genericIOGroup->c_se_nc_1IOSubgroup->ieeeStd754);
+ parseIec104Qos(&genericIOGroup->c_se_nc_1IOSubgroup->qos);
+ break;
+ }
+
+ case IEC104_ASDU_C_BO_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_bo_na_1IOGroup->ioa);
+ }
+ parseIec104Bsi(&genericIOGroup->c_bo_na_1IOSubgroup->bsi);
+ break;
+ }
+
+ case IEC104_ASDU_C_SC_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_sc_ta_1IOGroup->ioa);
+ }
+ parseIec104Sco(&genericIOGroup->c_sc_ta_1IOSubgroup->sco);
+ parseIec104Cp56Time2a(&genericIOGroup->c_sc_ta_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_DC_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_dc_ta_1IOGroup->ioa);
+ }
+ parseIec104Dco(&genericIOGroup->c_dc_ta_1IOSubgroup->dco);
+ parseIec104Cp56Time2a(&genericIOGroup->c_dc_ta_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_RC_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_rc_ta_1IOGroup->ioa);
+ }
+ parseIec104Rco(&genericIOGroup->c_rc_ta_1IOSubgroup->rco);
+ parseIec104Cp56Time2a(&genericIOGroup->c_rc_ta_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_se_ta_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->c_se_ta_1IOSubgroup->nva);
+ parseIec104Qos(&genericIOGroup->c_se_ta_1IOSubgroup->qos);
+ parseIec104Cp56Time2a(&genericIOGroup->c_se_ta_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_se_tb_1IOGroup->ioa);
+ }
+ parseIec104Sva(&genericIOGroup->c_se_tb_1IOSubgroup->sva);
+ parseIec104Qos(&genericIOGroup->c_se_tb_1IOSubgroup->qos);
+ parseIec104Cp56Time2a(&genericIOGroup->c_se_tb_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TC_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_se_tc_1IOGroup->ioa);
+ }
+ parseIec104IeeeStd754(&genericIOGroup->c_se_tc_1IOSubgroup->ieeeStd754);
+ parseIec104Qos(&genericIOGroup->c_se_tc_1IOSubgroup->qos);
+ parseIec104Cp56Time2a(&genericIOGroup->c_se_tc_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_BO_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_bo_ta_1IOGroup->ioa);
+ }
+ parseIec104Bsi(&genericIOGroup->c_bo_ta_1IOSubgroup->bsi);
+ parseIec104Cp56Time2a(&genericIOGroup->c_bo_ta_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_M_EI_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->m_ei_na_1IOGroup->ioa);
+ }
+ parseIec104Coi(&genericIOGroup->m_ei_na_1IOSubgroup->coi);
+ break;
+ }
+
+ case IEC104_ASDU_C_IC_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_ic_na_1IOGroup->ioa);
+ }
+ parseIec104Qoi(&genericIOGroup->c_ic_na_1IOSubgroup->qoi);
+ break;
+ }
+
+ case IEC104_ASDU_C_CI_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_ci_na_1IOGroup->ioa);
+ }
+ parseIec104Qcc(&genericIOGroup->c_ci_na_1IOSubgroup->qcc);
+ break;
+ }
+
+ case IEC104_ASDU_C_RD_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_rd_na_1IOGroup->ioa);
+ }
+ // no subgroup for this type
+ break;
+ }
+
+ case IEC104_ASDU_C_CS_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_cs_na_1IOGroup->ioa);
+ }
+ parseIec104Cp56Time2a(&genericIOGroup->c_cs_na_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_C_TS_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_ts_na_1IOGroup->ioa);
+ }
+ parseIec104Fbp(&genericIOGroup->c_ts_na_1IOSubgroup->fbp);
+ break;
+ }
+
+ case IEC104_ASDU_C_RP_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_rp_na_1IOGroup->ioa);
+ }
+ parseIec104Qrp(&genericIOGroup->c_rp_na_1IOSubgroup->qrp);
+ break;
+ }
+
+ case IEC104_ASDU_C_CD_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_cd_na_1IOGroup->ioa);
+ }
+ parseIec104Cp16Time2a(&genericIOGroup->c_cd_na_1IOSubgroup->msUpToSeconds);
+ break;
+ }
+
+ case IEC104_ASDU_C_TS_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->c_ts_ta_1IOGroup->ioa);
+ }
+ parseIec104Tsc(&genericIOGroup->c_ts_ta_1IOSubgroup->tsc);
+ parseIec104Cp56Time2a(&genericIOGroup->c_ts_ta_1IOSubgroup->sevenOctetBinaryTime);
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->p_me_na_1IOGroup->ioa);
+ }
+ parseIec104Nva(&genericIOGroup->p_me_na_1IOSubgroup->nva);
+ parseIec104Qpm(&genericIOGroup->p_me_na_1IOSubgroup->qpm);
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->p_me_nb_1IOGroup->ioa);
+ }
+ parseIec104Sva(&genericIOGroup->p_me_nb_1IOSubgroup->sva);
+ parseIec104Qpm(&genericIOGroup->p_me_nb_1IOSubgroup->qpm);
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NC_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->p_me_nc_1IOGroup->ioa);
+ }
+ parseIec104IeeeStd754(&genericIOGroup->p_me_nc_1IOSubgroup->ieeeStd754);
+ parseIec104Qpm(&genericIOGroup->p_me_nc_1IOSubgroup->qpm);
+ break;
+ }
+
+ case IEC104_ASDU_P_AC_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->p_ac_na_1IOGroup->ioa);
+ }
+ parseIec104Qpa(&genericIOGroup->p_ac_na_1IOSubgroup->qpa);
+ break;
+ }
+
+ case IEC104_ASDU_F_FR_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_fr_na_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_fr_na_1IOSubgroup->nameOfFile);
+ parseIec104Lof(&genericIOGroup->f_fr_na_1IOSubgroup->lengthOfFile);
+ parseIec104Frq(&genericIOGroup->f_fr_na_1IOSubgroup->frq);
+ break;
+ }
+
+ case IEC104_ASDU_F_SR_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_sr_na_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_sr_na_1IOSubgroup->nameOfFile);
+ parseIec104Nos(&genericIOGroup->f_sr_na_1IOSubgroup->nameOfSection);
+ parseIec104Lof(&genericIOGroup->f_sr_na_1IOSubgroup->lengthOfFileOrSection);
+ parseIec104Srq(&genericIOGroup->f_sr_na_1IOSubgroup->srq);
+ break;
+ }
+
+ case IEC104_ASDU_F_SC_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_sc_na_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_sc_na_1IOSubgroup->nameOfFile);
+ parseIec104Nos(&genericIOGroup->f_sc_na_1IOSubgroup->nameOfSection);
+ parseIec104Scq(&genericIOGroup->f_sc_na_1IOSubgroup->scq);
+ break;
+ }
+
+ case IEC104_ASDU_F_LS_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_ls_na_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_ls_na_1IOSubgroup->nameOfFile);
+ parseIec104Nos(&genericIOGroup->f_ls_na_1IOSubgroup->nameOfSection);
+ parseIec104Lsq(&genericIOGroup->f_ls_na_1IOSubgroup->lsq);
+ parseIec104Chs(&genericIOGroup->f_ls_na_1IOSubgroup->chs);
+ break;
+ }
+
+ case IEC104_ASDU_F_AF_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_af_na_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_af_na_1IOSubgroup->nameOfFile);
+ parseIec104Nos(&genericIOGroup->f_af_na_1IOSubgroup->nameOfSection);
+ parseIec104Afq(&genericIOGroup->f_af_na_1IOSubgroup->afq);
+ break;
+ }
+
+ case IEC104_ASDU_F_SG_NA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_sg_na_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_sg_na_1IOSubgroup->nameOfFile);
+ parseIec104Nos(&genericIOGroup->f_sg_na_1IOSubgroup->nameOfSection);
+ bool losValid = parseIec104Los(&genericIOGroup->f_sg_na_1IOSubgroup->lengthOfSegment,
+ genericIOGroup->apduSize);
+ // parse the segment when the LOS is deemed acceptable
+ if (losValid)
+ {
+ for (uint8_t i = 0; i < genericIOGroup->f_sg_na_1IOSubgroup->lengthOfSegment.lengthOfSegment; i++)
+ {
+ parseIec104Segment(&genericIOGroup->f_sg_na_1IOSubgroup->segment);
+ }
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_DR_TA_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_dr_ta_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_dr_ta_1IOSubgroup->nameOfFileOrSubdirectory);
+ parseIec104Lof(&genericIOGroup->f_dr_ta_1IOSubgroup->lengthOfFile);
+ parseIec104Sof(&genericIOGroup->f_dr_ta_1IOSubgroup->sof);
+ parseIec104Cp56Time2a(&genericIOGroup->f_dr_ta_1IOSubgroup->creationTimeOfFile);
+ break;
+ }
+
+ case IEC104_ASDU_F_SC_NB_1:
+ {
+ if (genericIOGroup->includeIOA)
+ {
+ parseIec104InformationObjectAddressWithThreeOctets(
+ &genericIOGroup->f_sc_nb_1IOGroup->ioa);
+ }
+ parseIec104Nof(&genericIOGroup->f_sc_nb_1IOSubgroup->nameOfFile);
+ parseIec104Cp56Time2a(&genericIOGroup->f_sc_nb_1IOSubgroup->startTime);
+ parseIec104Cp56Time2a(&genericIOGroup->f_sc_nb_1IOSubgroup->stopTime);
+ break;
+ }
+
+ default:
+ {
+ // passed ASDU type was not recognized
+ }
+ }
+}
+
+static void parseIec104GenericAsdu(uint32_t asduType, const Iec104ApciI* apci)
+{
+ uint8_t verifiedNumberOfElements = parseIec104Vsq(apci);
+ parseIec104CauseOfTx(apci);
+ parseIec104TwoOctetCommonAddress(apci);
+
+ // Set up the generic group structure
+ GenericIec104AsduIOGroup genericIOGroup;
+ genericIOGroup.asduType = asduType;
+ genericIOGroup.apduSize = apci->header.length;
+
+ // iterate over the reported number of elements overlaying the structures
+ for (uint32_t i = 0; i < verifiedNumberOfElements; i++)
+ {
+
+ //
+ // Handle Structure Qualifier == 1
+ //
+ if (apci->asdu.variableStructureQualifier.sq)
+ {
+ // IOA should only be printed on the first iteration in SQ1
+ if (i == 0)
+ {
+ genericIOGroup.includeIOA = true;
+ }
+ else
+ {
+ genericIOGroup.includeIOA = false;
+ }
+
+ // fill genericIOGroup with the appropriate asdu depending on the type
+ switch (asduType)
+ {
+ case IEC104_ASDU_M_SP_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_sp_na_1IOGroup = &apci->asdu.m_sp_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_SP_NA_1_IO_Subgroup* curIo = &apci->asdu.m_sp_na_1.subgroup + i;
+ genericIOGroup.m_sp_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_SP_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_DP_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_dp_na_1IOGroup = &apci->asdu.m_dp_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_DP_NA_1_IO_Subgroup* curIo = &apci->asdu.m_dp_na_1.subgroup + i;
+ genericIOGroup.m_dp_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_DP_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_ST_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_st_na_1IOGroup = &apci->asdu.m_st_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_ST_NA_1_IO_Subgroup* curIo = &apci->asdu.m_st_na_1.subgroup + i;
+ genericIOGroup.m_st_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_ST_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_BO_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_bo_na_1IOGroup = &apci->asdu.m_bo_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_BO_NA_1_IO_Subgroup* curIo = &apci->asdu.m_bo_na_1.subgroup + i;
+ genericIOGroup.m_bo_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_BO_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_ME_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_me_na_1IOGroup = &apci->asdu.m_me_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_ME_NA_1_IO_Subgroup* curIo = &apci->asdu.m_me_na_1.subgroup + i;
+ genericIOGroup.m_me_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_ME_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_ME_NB_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_me_nb_1IOGroup = &apci->asdu.m_me_nb_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_ME_NB_1_IO_Subgroup* curIo = &apci->asdu.m_me_nb_1.subgroup + i;
+ genericIOGroup.m_me_nb_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_ME_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_ME_NC_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_me_nc_1IOGroup = &apci->asdu.m_me_nc_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_ME_NC_1_IO_Subgroup* curIo = &apci->asdu.m_me_nc_1.subgroup + i;
+ genericIOGroup.m_me_nc_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_ME_TC_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_IT_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_it_na_1IOGroup = &apci->asdu.m_it_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_IT_NA_1_IO_Subgroup* curIo = &apci->asdu.m_it_na_1.subgroup + i;
+ genericIOGroup.m_it_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_IT_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EP_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EP_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EP_TC_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_M_PS_NA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_ps_na_1IOGroup = &apci->asdu.m_ps_na_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_PS_NA_1_IO_Subgroup* curIo = &apci->asdu.m_ps_na_1.subgroup + i;
+ genericIOGroup.m_ps_na_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_ND_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.m_me_nd_1IOGroup = &apci->asdu.m_me_nd_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104M_ME_ND_1_IO_Subgroup* curIo = &apci->asdu.m_me_nd_1.subgroup + i;
+ genericIOGroup.m_me_nd_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_M_SP_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_DP_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_ST_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_BO_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_ME_TD_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_ME_TE_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_ME_TF_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_IT_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EP_TD_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EP_TE_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EP_TF_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SC_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_DC_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_RC_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SE_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SE_NB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SE_NC_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_BO_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SC_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_DC_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_RC_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SE_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SE_TB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_SE_TC_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_BO_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_M_EI_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_IC_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_CI_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_RD_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_CS_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_TS_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_RP_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_CD_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_C_TS_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ //case IEC104_ASDU_P_ME_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ //case IEC104_ASDU_P_ME_NB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ //case IEC104_ASDU_P_ME_NC_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_P_AC_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_F_FR_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_F_SR_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_F_SC_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_F_LS_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_F_AF_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ // case IEC104_ASDU_F_SG_NA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_F_DR_TA_1:
+ {
+ // Since there is only one full IOGroup structure in SQ1 this can stay for all cases
+ genericIOGroup.f_dr_ta_1IOGroup = &apci->asdu.f_dr_ta_1;
+
+ // the subgroup pointer can be calculated by incrementing the first subgroup pointer by the iteration times the size of the subgroup pointer
+ // since `i` will be 0 on the first go round this works for all iterations
+ // since C adds based on the pointer type we only need to cast and increment
+ const Iec104F_DR_TA_1_IO_Subgroup* curIo = &apci->asdu.f_dr_ta_1.subgroup + i;
+ genericIOGroup.f_dr_ta_1IOSubgroup = curIo;
+
+ break;
+ }
+
+ // case IEC104_ASDU_F_SC_NB_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ default:
+ {
+ // SQ1 ASDU parsing not implemented for this type
+ }
+ }
+
+ // parse the new subgroup
+ parseIec104GenericIOGroup(&genericIOGroup);
+
+ }
+ //
+ // Handle Structure Qualifier == 0
+ //
+ else
+ {
+ // the IOA should always be included for SQ0
+ genericIOGroup.includeIOA = true;
+
+ // fill genericIOGroup with the appropriate asdu depending on the type
+ switch (asduType)
+ {
+ case IEC104_ASDU_M_SP_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_SP_NA_1_IO_Group struct
+ const Iec104M_SP_NA_1_IO_Group* curIo =
+ (const Iec104M_SP_NA_1_IO_Group*) &apci->asdu.m_sp_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_sp_na_1IOGroup = curIo;
+ genericIOGroup.m_sp_na_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_SP_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_SP_TA_1_IO_Group struct
+ const Iec104M_SP_TA_1_IO_Group* curIo =
+ (const Iec104M_SP_TA_1_IO_Group*) &apci->asdu.m_sp_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_sp_ta_1IOGroup = curIo;
+ genericIOGroup.m_sp_ta_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_DP_NA_1_IO_Group struct
+ const Iec104M_DP_NA_1_IO_Group* curIo =
+ (const Iec104M_DP_NA_1_IO_Group*) &apci->asdu.m_dp_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_dp_na_1IOGroup = curIo;
+ genericIOGroup.m_dp_na_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_DP_TA_1_IO_Group struct
+ const Iec104M_DP_TA_1_IO_Group* curIo =
+ (const Iec104M_DP_TA_1_IO_Group*) &apci->asdu.m_dp_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_dp_ta_1IOGroup = curIo;
+ genericIOGroup.m_dp_ta_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_ST_NA_1_IO_Group struct
+ const Iec104M_ST_NA_1_IO_Group* curIo =
+ (const Iec104M_ST_NA_1_IO_Group*) &apci->asdu.m_st_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_st_na_1IOGroup = curIo;
+ genericIOGroup.m_st_na_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_ST_TA_1_IO_Group struct
+ const Iec104M_ST_TA_1_IO_Group* curIo =
+ (const Iec104M_ST_TA_1_IO_Group*) &apci->asdu.m_st_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_st_ta_1IOGroup = curIo;
+ genericIOGroup.m_st_ta_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_BO_NA_1_IO_Group struct
+ const Iec104M_BO_NA_1_IO_Group* curIo =
+ (const Iec104M_BO_NA_1_IO_Group*) &apci->asdu.m_bo_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_bo_na_1IOGroup = curIo;
+ genericIOGroup.m_bo_na_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_BO_TA_1_IO_Group struct
+ const Iec104M_BO_TA_1_IO_Group* curIo =
+ (const Iec104M_BO_TA_1_IO_Group*) &apci->asdu.m_bo_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_bo_ta_1IOGroup = curIo;
+ genericIOGroup.m_bo_ta_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_NA_1_IO_Group struct
+ const Iec104M_ME_NA_1_IO_Group* curIo =
+ (const Iec104M_ME_NA_1_IO_Group*) &apci->asdu.m_me_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_na_1IOGroup = curIo;
+ genericIOGroup.m_me_na_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_TA_1_IO_Group struct
+ const Iec104M_ME_TA_1_IO_Group* curIo =
+ (const Iec104M_ME_TA_1_IO_Group*) &apci->asdu.m_me_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_ta_1IOGroup = curIo;
+ genericIOGroup.m_me_ta_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NB_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_NB_1_IO_Group struct
+ const Iec104M_ME_NB_1_IO_Group* curIo =
+ (const Iec104M_ME_NB_1_IO_Group*) &apci->asdu.m_me_nb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_nb_1IOGroup = curIo;
+ genericIOGroup.m_me_nb_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_TB_1_IO_Group struct
+ const Iec104M_ME_TB_1_IO_Group* curIo =
+ (const Iec104M_ME_TB_1_IO_Group*) &apci->asdu.m_me_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_tb_1IOGroup = curIo;
+ genericIOGroup.m_me_tb_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NC_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_NC_1_IO_Group struct
+ const Iec104M_ME_NC_1_IO_Group* curIo =
+ (const Iec104M_ME_NC_1_IO_Group*) &apci->asdu.m_me_nc_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_nc_1IOGroup = curIo;
+ genericIOGroup.m_me_nc_1IOSubgroup = &curIo->subgroup;
+
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TC_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_TC_1_IO_Group struct
+ const Iec104M_ME_TC_1_IO_Group* curIo =
+ (const Iec104M_ME_TC_1_IO_Group*) &apci->asdu.m_me_tc_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_tc_1IOGroup = curIo;
+ genericIOGroup.m_me_tc_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_IT_NA_1_IO_Group struct
+ const Iec104M_IT_NA_1_IO_Group* curIo =
+ (const Iec104M_IT_NA_1_IO_Group*) &apci->asdu.m_it_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_it_na_1IOGroup = curIo;
+ genericIOGroup.m_it_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_IT_TA_1_IO_Group struct
+ const Iec104M_IT_TA_1_IO_Group* curIo =
+ (const Iec104M_IT_TA_1_IO_Group*) &apci->asdu.m_it_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_it_ta_1IOGroup = curIo;
+ genericIOGroup.m_it_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TA_1:
+ {
+ // increment the information object block pointer by the size of the M_EP_TA_1_IO_Group struct
+ const Iec104M_EP_TA_1_IO_Group* curIo =
+ (const Iec104M_EP_TA_1_IO_Group*) &apci->asdu.m_ep_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ep_ta_1IOGroup = curIo;
+ genericIOGroup.m_ep_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_EP_TB_1_IO_Group struct
+ const Iec104M_EP_TB_1_IO_Group* curIo =
+ (const Iec104M_EP_TB_1_IO_Group*) &apci->asdu.m_ep_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ep_tb_1IOGroup = curIo;
+ genericIOGroup.m_ep_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TC_1:
+ {
+ // increment the information object block pointer by the size of the M_EP_TC_1_IO_Group struct
+ const Iec104M_EP_TC_1_IO_Group* curIo =
+ (const Iec104M_EP_TC_1_IO_Group*) &apci->asdu.m_ep_tc_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ep_tc_1IOGroup = curIo;
+ genericIOGroup.m_ep_tc_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_PS_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_PS_NA_1_IO_Group struct
+ const Iec104M_PS_NA_1_IO_Group* curIo =
+ (const Iec104M_PS_NA_1_IO_Group*) &apci->asdu.m_ps_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ps_na_1IOGroup = curIo;
+ genericIOGroup.m_ps_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_ND_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_ND_1_IO_Group struct
+ const Iec104M_ME_ND_1_IO_Group* curIo =
+ (const Iec104M_ME_ND_1_IO_Group*) &apci->asdu.m_me_nd_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_nd_1IOGroup = curIo;
+ genericIOGroup.m_me_nd_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_SP_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_SP_TB_1_IO_Group struct
+ const Iec104M_SP_TB_1_IO_Group* curIo =
+ (const Iec104M_SP_TB_1_IO_Group*) &apci->asdu.m_sp_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_sp_tb_1IOGroup = curIo;
+ genericIOGroup.m_sp_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_DP_TB_1_IO_Group struct
+ const Iec104M_DP_TB_1_IO_Group* curIo =
+ (const Iec104M_DP_TB_1_IO_Group*) &apci->asdu.m_dp_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_dp_tb_1IOGroup = curIo;
+ genericIOGroup.m_dp_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_ST_TB_1_IO_Group struct
+ const Iec104M_ST_TB_1_IO_Group* curIo =
+ (const Iec104M_ST_TB_1_IO_Group*) &apci->asdu.m_st_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_st_tb_1IOGroup = curIo;
+ genericIOGroup.m_st_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_BO_TB_1_IO_Group struct
+ const Iec104M_BO_TB_1_IO_Group* curIo =
+ (const Iec104M_BO_TB_1_IO_Group*) &apci->asdu.m_bo_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_bo_tb_1IOGroup = curIo;
+ genericIOGroup.m_bo_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TD_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_TD_1_IO_Group struct
+ const Iec104M_ME_TD_1_IO_Group* curIo =
+ (const Iec104M_ME_TD_1_IO_Group*) &apci->asdu.m_me_td_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_td_1IOGroup = curIo;
+ genericIOGroup.m_me_td_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TE_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_TE_1_IO_Group struct
+ const Iec104M_ME_TE_1_IO_Group* curIo =
+ (const Iec104M_ME_TE_1_IO_Group*) &apci->asdu.m_me_te_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_te_1IOGroup = curIo;
+ genericIOGroup.m_me_te_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TF_1:
+ {
+ // increment the information object block pointer by the size of the M_ME_TF_1_IO_Group struct
+ const Iec104M_ME_TF_1_IO_Group* curIo =
+ (const Iec104M_ME_TF_1_IO_Group*) &apci->asdu.m_me_tf_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_me_tf_1IOGroup = curIo;
+ genericIOGroup.m_me_tf_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_TB_1:
+ {
+ // increment the information object block pointer by the size of the M_IT_TB_1_IO_Group struct
+ const Iec104M_IT_TB_1_IO_Group* curIo =
+ (const Iec104M_IT_TB_1_IO_Group*) &apci->asdu.m_it_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_it_tb_1IOGroup = curIo;
+ genericIOGroup.m_it_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TD_1:
+ {
+ // increment the information object block pointer by the size of the M_EP_TD_1_IO_Group struct
+ const Iec104M_EP_TD_1_IO_Group* curIo =
+ (const Iec104M_EP_TD_1_IO_Group*) &apci->asdu.m_ep_td_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ep_td_1IOGroup = curIo;
+ genericIOGroup.m_ep_td_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TE_1:
+ {
+ // increment the information object block pointer by the size of the M_EP_TE_1_IO_Group struct
+ const Iec104M_EP_TE_1_IO_Group* curIo =
+ (const Iec104M_EP_TE_1_IO_Group*) &apci->asdu.m_ep_te_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ep_te_1IOGroup = curIo;
+ genericIOGroup.m_ep_te_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TF_1:
+ {
+ // increment the information object block pointer by the size of the M_EP_TF_1_IO_Group struct
+ const Iec104M_EP_TF_1_IO_Group* curIo =
+ (const Iec104M_EP_TF_1_IO_Group*) &apci->asdu.m_ep_tf_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ep_tf_1IOGroup = curIo;
+ genericIOGroup.m_ep_tf_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SC_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_SC_NA_1_IO_Group struct
+ const Iec104C_SC_NA_1_IO_Group* curIo =
+ (const Iec104C_SC_NA_1_IO_Group*) &apci->asdu.c_sc_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_sc_na_1IOGroup = curIo;
+ genericIOGroup.c_sc_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_DC_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_DC_NA_1_IO_Group struct
+ const Iec104C_DC_NA_1_IO_Group* curIo =
+ (const Iec104C_DC_NA_1_IO_Group*) &apci->asdu.c_dc_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_dc_na_1IOGroup = curIo;
+ genericIOGroup.c_dc_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_RC_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_RC_NA_1_IO_Group struct
+ const Iec104C_RC_NA_1_IO_Group* curIo =
+ (const Iec104C_RC_NA_1_IO_Group*) &apci->asdu.c_rc_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_rc_na_1IOGroup = curIo;
+ genericIOGroup.c_rc_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_SE_NA_1_IO_Group struct
+ const Iec104C_SE_NA_1_IO_Group* curIo =
+ (const Iec104C_SE_NA_1_IO_Group*) &apci->asdu.c_se_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_se_na_1IOGroup = curIo;
+ genericIOGroup.c_se_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NB_1:
+ {
+ // increment the information object block pointer by the size of the C_SE_NB_1_IO_Group struct
+ const Iec104C_SE_NB_1_IO_Group* curIo =
+ (const Iec104C_SE_NB_1_IO_Group*) &apci->asdu.c_se_nb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_se_nb_1IOGroup = curIo;
+ genericIOGroup.c_se_nb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NC_1:
+ {
+ // increment the information object block pointer by the size of the C_SE_NC_1_IO_Group struct
+ const Iec104C_SE_NC_1_IO_Group* curIo =
+ (const Iec104C_SE_NC_1_IO_Group*) &apci->asdu.c_se_nc_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_se_nc_1IOGroup = curIo;
+ genericIOGroup.c_se_nc_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_BO_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_BO_NA_1_IO_Group struct
+ const Iec104C_BO_NA_1_IO_Group* curIo =
+ (const Iec104C_BO_NA_1_IO_Group*) &apci->asdu.c_bo_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_bo_na_1IOGroup = curIo;
+ genericIOGroup.c_bo_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SC_TA_1:
+ {
+ // increment the information object block pointer by the size of the C_SC_TA_1_IO_Group struct
+ const Iec104C_SC_TA_1_IO_Group* curIo =
+ (const Iec104C_SC_TA_1_IO_Group*) &apci->asdu.c_sc_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_sc_ta_1IOGroup = curIo;
+ genericIOGroup.c_sc_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_DC_TA_1:
+ {
+ // increment the information object block pointer by the size of the C_DC_TA_1_IO_Group struct
+ const Iec104C_DC_TA_1_IO_Group* curIo =
+ (const Iec104C_DC_TA_1_IO_Group*) &apci->asdu.c_dc_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_dc_ta_1IOGroup = curIo;
+ genericIOGroup.c_dc_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_RC_TA_1:
+ {
+ // increment the information object block pointer by the size of the C_RC_TA_1_IO_Group struct
+ const Iec104C_RC_TA_1_IO_Group* curIo =
+ (const Iec104C_RC_TA_1_IO_Group*) &apci->asdu.c_rc_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_rc_ta_1IOGroup = curIo;
+ genericIOGroup.c_rc_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TA_1:
+ {
+ // increment the information object block pointer by the size of the C_SE_TA_1_IO_Group struct
+ const Iec104C_SE_TA_1_IO_Group* curIo =
+ (const Iec104C_SE_TA_1_IO_Group*) &apci->asdu.c_se_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_se_ta_1IOGroup = curIo;
+ genericIOGroup.c_se_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TB_1:
+ {
+ // increment the information object block pointer by the size of the C_SE_TB_1_IO_Group struct
+ const Iec104C_SE_TB_1_IO_Group* curIo =
+ (const Iec104C_SE_TB_1_IO_Group*) &apci->asdu.c_se_tb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_se_tb_1IOGroup = curIo;
+ genericIOGroup.c_se_tb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TC_1:
+ {
+ // increment the information object block pointer by the size of the C_SE_TC_1_IO_Group struct
+ const Iec104C_SE_TC_1_IO_Group* curIo =
+ (const Iec104C_SE_TC_1_IO_Group*) &apci->asdu.c_se_tc_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_se_tc_1IOGroup = curIo;
+ genericIOGroup.c_se_tc_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_BO_TA_1:
+ {
+ // increment the information object block pointer by the size of the C_BO_TA_1_IO_Group struct
+ const Iec104C_BO_TA_1_IO_Group* curIo =
+ (const Iec104C_BO_TA_1_IO_Group*) &apci->asdu.c_bo_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_bo_ta_1IOGroup = curIo;
+ genericIOGroup.c_bo_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_M_EI_NA_1:
+ {
+ // increment the information object block pointer by the size of the M_EI_NA_1_IO_Group struct
+ const Iec104M_EI_NA_1_IO_Group* curIo =
+ (const Iec104M_EI_NA_1_IO_Group*) &apci->asdu.m_ei_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.m_ei_na_1IOGroup = curIo;
+ genericIOGroup.m_ei_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_IC_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_IC_NA_1_IO_Group struct
+ const Iec104C_IC_NA_1_IO_Group* curIo =
+ (const Iec104C_IC_NA_1_IO_Group*) &apci->asdu.c_ic_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_ic_na_1IOGroup = curIo;
+ genericIOGroup.c_ic_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_CI_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_CI_NA_1_IO_Group struct
+ const Iec104C_CI_NA_1_IO_Group* curIo =
+ (const Iec104C_CI_NA_1_IO_Group*) &apci->asdu.c_ci_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_ci_na_1IOGroup = curIo;
+ genericIOGroup.c_ci_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_RD_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_RD_NA_1_IO_Group struct
+ const Iec104C_RD_NA_1_IO_Group* curIo =
+ (const Iec104C_RD_NA_1_IO_Group*) &apci->asdu.c_rd_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_rd_na_1IOGroup = curIo;
+ genericIOGroup.c_rd_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_CS_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_CS_NA_1_IO_Group struct
+ const Iec104C_CS_NA_1_IO_Group* curIo =
+ (const Iec104C_CS_NA_1_IO_Group*) &apci->asdu.c_cs_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_cs_na_1IOGroup = curIo;
+ genericIOGroup.c_cs_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_TS_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_TS_NA_1_IO_Group struct
+ const Iec104C_TS_NA_1_IO_Group* curIo =
+ (const Iec104C_TS_NA_1_IO_Group*) &apci->asdu.c_ts_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_ts_na_1IOGroup = curIo;
+ genericIOGroup.c_ts_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_RP_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_RP_NA_1_IO_Group struct
+ const Iec104C_RP_NA_1_IO_Group* curIo =
+ (const Iec104C_RP_NA_1_IO_Group*) &apci->asdu.c_rp_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_rp_na_1IOGroup = curIo;
+ genericIOGroup.c_rp_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_CD_NA_1:
+ {
+ // increment the information object block pointer by the size of the C_CD_NA_1_IO_Group struct
+ const Iec104C_CD_NA_1_IO_Group* curIo =
+ (const Iec104C_CD_NA_1_IO_Group*) &apci->asdu.c_cd_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_cd_na_1IOGroup = curIo;
+ genericIOGroup.c_cd_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_C_TS_TA_1:
+ {
+ // increment the information object block pointer by the size of the C_TS_TA_1_IO_Group struct
+ const Iec104C_TS_TA_1_IO_Group* curIo =
+ (const Iec104C_TS_TA_1_IO_Group*) &apci->asdu.c_ts_ta_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.c_ts_ta_1IOGroup = curIo;
+ genericIOGroup.c_ts_ta_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NA_1:
+ {
+ // increment the information object block pointer by the size of the P_ME_NA_1_IO_Group struct
+ const Iec104P_ME_NA_1_IO_Group* curIo =
+ (const Iec104P_ME_NA_1_IO_Group*) &apci->asdu.p_me_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.p_me_na_1IOGroup = curIo;
+ genericIOGroup.p_me_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NB_1:
+ {
+ // increment the information object block pointer by the size of the P_ME_NB_1_IO_Group struct
+ const Iec104P_ME_NB_1_IO_Group* curIo =
+ (const Iec104P_ME_NB_1_IO_Group*) &apci->asdu.p_me_nb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.p_me_nb_1IOGroup = curIo;
+ genericIOGroup.p_me_nb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NC_1:
+ {
+ // increment the information object block pointer by the size of the P_ME_NC_1_IO_Group struct
+ const Iec104P_ME_NC_1_IO_Group* curIo =
+ (const Iec104P_ME_NC_1_IO_Group*) &apci->asdu.p_me_nc_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.p_me_nc_1IOGroup = curIo;
+ genericIOGroup.p_me_nc_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_P_AC_NA_1:
+ {
+ // increment the information object block pointer by the size of the P_AC_NA_1_IO_Group struct
+ const Iec104P_AC_NA_1_IO_Group* curIo =
+ (const Iec104P_AC_NA_1_IO_Group*) &apci->asdu.p_ac_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.p_ac_na_1IOGroup = curIo;
+ genericIOGroup.p_ac_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_F_FR_NA_1:
+ {
+ // increment the information object block pointer by the size of the F_FR_NA_1_IO_Group struct
+ const Iec104F_FR_NA_1_IO_Group* curIo =
+ (const Iec104F_FR_NA_1_IO_Group*) &apci->asdu.f_fr_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_fr_na_1IOGroup = curIo;
+ genericIOGroup.f_fr_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_F_SR_NA_1:
+ {
+ // increment the information object block pointer by the size of the F_SR_NA_1_IO_Group struct
+ const Iec104F_SR_NA_1_IO_Group* curIo =
+ (const Iec104F_SR_NA_1_IO_Group*) &apci->asdu.f_sr_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_sr_na_1IOGroup = curIo;
+ genericIOGroup.f_sr_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_F_SC_NA_1:
+ {
+ // increment the information object block pointer by the size of the F_SC_NA_1_IO_Group struct
+ const Iec104F_SC_NA_1_IO_Group* curIo =
+ (const Iec104F_SC_NA_1_IO_Group*) &apci->asdu.f_sc_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_sc_na_1IOGroup = curIo;
+ genericIOGroup.f_sc_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_F_LS_NA_1:
+ {
+ // increment the information object block pointer by the size of the F_LS_NA_1_IO_Group struct
+ const Iec104F_LS_NA_1_IO_Group* curIo =
+ (const Iec104F_LS_NA_1_IO_Group*) &apci->asdu.f_ls_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_ls_na_1IOGroup = curIo;
+ genericIOGroup.f_ls_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_F_AF_NA_1:
+ {
+ // increment the information object block pointer by the size of the F_AF_NA_1_IO_Group struct
+ const Iec104F_AF_NA_1_IO_Group* curIo =
+ (const Iec104F_AF_NA_1_IO_Group*) &apci->asdu.f_af_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_af_na_1IOGroup = curIo;
+ genericIOGroup.f_af_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ case IEC104_ASDU_F_SG_NA_1:
+ {
+ // increment the information object block pointer by the size of the F_SG_NA_1_IO_Group struct
+ const Iec104F_SG_NA_1_IO_Group* curIo =
+ (const Iec104F_SG_NA_1_IO_Group*) &apci->asdu.f_sg_na_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_sg_na_1IOGroup = curIo;
+ genericIOGroup.f_sg_na_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ // case IEC104_ASDU_F_DR_TA_1
+ // path doesn't happen as it gets caught during the ASDU check
+
+ case IEC104_ASDU_F_SC_NB_1:
+ {
+ // increment the information object block pointer by the size of the F_SC_NB_1_IO_Group struct
+ const Iec104F_SC_NB_1_IO_Group* curIo =
+ (const Iec104F_SC_NB_1_IO_Group*) &apci->asdu.f_sc_nb_1 + i;
+
+ // print the SQ0 IO block
+ genericIOGroup.f_sc_nb_1IOGroup = curIo;
+ genericIOGroup.f_sc_nb_1IOSubgroup = &curIo->subgroup;
+ break;
+ }
+
+ default:
+ {
+ // SQ0 ASDU parsing not implemented for this type
+ }
+ }
+
+ // parse the group
+ parseIec104GenericIOGroup(&genericIOGroup);
+ }
+ }
+}
+
+void parseIec104ApciU(const Iec104ApciU* apci)
+{
+ // throw an alert if the start value is not 0x68
+ if (apci->header.start != IEC104_START_BYTE)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_START);
+ }
+ // throw an alert if any length other than 0x04 is provided since this APCI can only have 4 bytes of data
+ // a similar length check is performed in `iec104.c` when determining packet size. It is possible for that check to pass and this one alert
+ else if (apci->header.length != IEC104_APCI_TYPE_U_LEN)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_LENGTH);
+ }
+ // initial APCI validation has passed
+ else
+ {
+ // alert on use of reserved field
+ if (apci->reserved1 || apci->reserved2 || apci->reserved3)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCIU_RESERVED_FIELD_IN_USE);
+ }
+
+ // make sure that only one of the fields is set
+ uint32_t setCount = 0;
+ setCount += apci->startdtAct;
+ setCount += apci->startdtCon;
+ setCount += apci->stopdtAct;
+ setCount += apci->stopdtCon;
+ setCount += apci->testfrAct;
+ setCount += apci->testfrCon;
+ if (setCount == 0 || setCount > 1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCIU_INVALID_MESSAGE_TYPE);
+ }
+ }
+}
+
+void parseIec104ApciS(const Iec104ApciS* apci)
+{
+ // throw an alert if the start value is not 0x68
+ if (apci->header.start != IEC104_START_BYTE)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_START);
+ }
+ // throw an alert if any length other than 0x04 is provided since this APCI can only have 4 bytes of data
+ // a similar length check is performed in `iec104.c` when determining packet size. It is possible for that check to pass and this one alert
+ else if (apci->header.length != IEC104_APCI_TYPE_S_LEN)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_LENGTH);
+ }
+ // initial APCI validation has passed
+ else
+ {
+ // alert on use of reserved field
+ if (apci->reserved1 || apci->reserved2)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCIS_RESERVED_FIELD_IN_USE);
+ }
+ }
+}
+
+void parseIec104ApciI(const Iec104ApciI* apci)
+{
+ // throw an alert if the start value is not 0x68
+ if (apci->header.start != IEC104_START_BYTE)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_START);
+ }
+ // throw an alert if any length under 12 is detected as that is the smallest possible message according to the spec
+ // a similar length check is performed in `iec104.c` when determining packet size. It is possible for that check to pass and this one alert
+ else if (apci->header.length < IEC104_APCI_TYPE_I_MIN_LEN)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_BAD_LENGTH);
+ }
+ // initial APCI validation has passed
+ else
+ {
+ // set up the ASDU check structure
+ Iec104AsduCheck curAsduCheck;
+ curAsduCheck.apci = apci;
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0 };
+
+ // select the appropriate asdu based on typeId value
+ switch (apci->asdu.typeId)
+ {
+
+ case IEC104_ASDU_M_SP_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_SP_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_SP_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_SP_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_DP_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_DP_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ST_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ST_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_BO_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_BO_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.percyc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.percyc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_NB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_NC_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.percyc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_NC_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TC_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_TC_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqcogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco4 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_IT_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqcogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco4 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_IT_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EP_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EP_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TC_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EP_TC_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_PS_NA_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_PS_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_ND_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.percyc = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.back = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_ND_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_SP_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_SP_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_DP_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_DP_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ST_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retrem = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.retloc = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ST_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_BO_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_BO_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TD_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_TD_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TE_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_TE_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_ME_TF_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_ME_TF_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_IT_TB_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqcogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.reqco4 = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_IT_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TD_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EP_TD_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TE_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EP_TE_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EP_TF_1:
+ {
+ // run checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EP_TF_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SC_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SC_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_DC_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_DC_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_RC_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_RC_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SE_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NB_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SE_NB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_NC_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SE_NC_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_BO_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_BO_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SC_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SC_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_DC_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_DC_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_RC_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_RC_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SE_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TB_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SE_TB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_SE_TC_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_SE_TC_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_BO_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_BO_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_M_EI_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.init = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_M_EI_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_IC_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_IC_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_CI_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actterm = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_CI_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_RD_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_RD_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_CS_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_CS_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_TS_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_TS_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_RP_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_RP_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_CD_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_CD_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_C_TS_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_C_TS_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_P_ME_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NB_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_P_ME_NB_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_P_ME_NC_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inrogen = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro1 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro2 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro3 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro4 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro5 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro6 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro7 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro8 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro9 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro10 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro11 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro12 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro13 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro14 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro15 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.inro16 = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_P_ME_NC_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_P_AC_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.act = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.actcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deact = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.deactcon = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_P_AC_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_FR_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_FR_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_SR_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_SR_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_SC_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_SC_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_LS_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_LS_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_AF_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_AF_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_SG_NA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_SG_NA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_DR_TA_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = false;
+ curAsduCheck.sq1Allowed = true;
+ curAsduCheck.multipleIOAllowed = true;
+ curAsduCheck.checkCauseOfTx.spont = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.req = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_DR_TA_1, apci);
+ }
+ break;
+ }
+
+ case IEC104_ASDU_F_SC_NB_1:
+ {
+ // run generic checks against the asdu before continuing
+ curAsduCheck.sq0Allowed = true;
+ curAsduCheck.sq1Allowed = false;
+ curAsduCheck.multipleIOAllowed = false;
+ curAsduCheck.checkCauseOfTx.file = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_type_id = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_cause_tx = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_common_addr = IEC104_CTX_ALLOWED;
+ curAsduCheck.checkCauseOfTx.unk_info_addr = IEC104_CTX_ALLOWED;
+
+ if (checkIec104Asdu(curAsduCheck))
+ {
+ // parse the asdu if checks pass
+ parseIec104GenericAsdu(IEC104_ASDU_F_SC_NB_1, apci);
+ }
+ break;
+ }
+
+ default:
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_ASDU_TYPE);
+ break;
+ }
+ }
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_parse_apdu.h author Jared Rittle <jared.rittle@cisco.com>
+
+#ifndef IEC104_PARSE_APCI_H
+#define IEC104_PARSE_APCI_H
+
+#include <stdint.h>
+
+void parseIec104ApciU(const struct Iec104ApciU* apci);
+void parseIec104ApciS(const struct Iec104ApciS* apci);
+void parseIec104ApciI(const struct Iec104ApciI* apci);
+
+
+#define IEC104_ERROR -1
+#define IEC104_START_BYTE 0x68
+#define IEC104_APCI_HDR_LEN 2 // accounts for Start and Length
+#define IEC104_APCI_TYPE_U_LEN 4
+#define IEC104_APCI_TYPE_S_LEN 4
+#define IEC104_APCI_TYPE_I_MIN_LEN 12
+#define IEC104_APCI_TYPE_I_HDR_LEN 6
+#define IEC104_CTX_NOT_ALLOWED 0
+#define IEC104_CTX_ALLOWED 1
+
+
+//
+//
+// Enums
+//
+//
+
+// Definition for the different APCI headers
+// This allows us to make determinations on how to handle the fields following the first 1-2 bits
+enum ApciTypeEnum
+{
+ IEC104_NO_APCI = -1,
+ IEC104_APCI_TYPE_I = 0,
+ IEC104_APCI_TYPE_S = 1,
+ IEC104_APCI_TYPE_U = 2,
+};
+
+enum AsduTypeEnum
+{
+ IEC104_NO_ASDU = 0, // placeholder for an error case
+ IEC104_ASDU_M_SP_NA_1 = 1, // Single-point information
+ IEC104_ASDU_M_SP_TA_1 = 2, // Single-point information with time tag
+ IEC104_ASDU_M_DP_NA_1 = 3, // Double-point information
+ IEC104_ASDU_M_DP_TA_1 = 4, // Double-point information with time tag
+ IEC104_ASDU_M_ST_NA_1 = 5, // Step position information
+ IEC104_ASDU_M_ST_TA_1 = 6, // Step position information with time tag
+ IEC104_ASDU_M_BO_NA_1 = 7, // Bitstring of 32 bit
+ IEC104_ASDU_M_BO_TA_1 = 8, // Bitstring of 32 bit with time tag
+ IEC104_ASDU_M_ME_NA_1 = 9, // Measured value, normalized value
+ IEC104_ASDU_M_ME_TA_1 = 10, // Measured value, normalized value with time tag
+ IEC104_ASDU_M_ME_NB_1 = 11, // Measured value, scaled value
+ IEC104_ASDU_M_ME_TB_1 = 12, // Measured value, scaled value wit time tag
+ IEC104_ASDU_M_ME_NC_1 = 13, // Measured value, short floating point number
+ IEC104_ASDU_M_ME_TC_1 = 14, // Measured value, short floating point number with time tag
+ IEC104_ASDU_M_IT_NA_1 = 15, // Integrated totals
+ IEC104_ASDU_M_IT_TA_1 = 16, // Integrated totals with time tag
+ IEC104_ASDU_M_EP_TA_1 = 17, // Event of protection equipment with time tag
+ IEC104_ASDU_M_EP_TB_1 = 18, // Packed start events of protection equipment with time tag
+ IEC104_ASDU_M_EP_TC_1 = 19, // Packed output circuit information of protection equipment with time tag
+ IEC104_ASDU_M_PS_NA_1 = 20, // Packed single point information with status change detection
+ IEC104_ASDU_M_ME_ND_1 = 21, // Measured value, normalized value without quality descriptor
+ // 22-29 reserved
+ IEC104_ASDU_M_SP_TB_1 = 30, // Single-point information with time tag CP56Time2a
+ IEC104_ASDU_M_DP_TB_1 = 31, // Double-point information with time tag CP56Time2a
+ IEC104_ASDU_M_ST_TB_1 = 32, // Step position information with time tag CP56Time2a
+ IEC104_ASDU_M_BO_TB_1 = 33, // Bitstring of 32 bit with time tag CP56Time2a
+ IEC104_ASDU_M_ME_TD_1 = 34, // Measured value, normalized value with time tag CP56Time2a
+ IEC104_ASDU_M_ME_TE_1 = 35, // Measured value, scaled value with time tag CP56Time2a
+ IEC104_ASDU_M_ME_TF_1 = 36, // Measured value, short floating point number with time tag CP56Time2a
+ IEC104_ASDU_M_IT_TB_1 = 37, // Integrated totals with time tag CP56Time2a
+ IEC104_ASDU_M_EP_TD_1 = 38, // Event of protection equipment with time tag CP56Time2a
+ IEC104_ASDU_M_EP_TE_1 = 39, // Packed start events of protection equipment with time tag CP56Time2a
+ IEC104_ASDU_M_EP_TF_1 = 40, // Packed output circuit information of protection equipment with time tag CP56Time2a
+ // 41-44 reserved
+ IEC104_ASDU_C_SC_NA_1 = 45, // Single command
+ IEC104_ASDU_C_DC_NA_1 = 46, // Double command
+ IEC104_ASDU_C_RC_NA_1 = 47, // Regulating step command
+ IEC104_ASDU_C_SE_NA_1 = 48, // Set-point Command, normalized value
+ IEC104_ASDU_C_SE_NB_1 = 49, // Set-point Command, scaled value
+ IEC104_ASDU_C_SE_NC_1 = 50, // Set-point Command, short floating point number
+ IEC104_ASDU_C_BO_NA_1 = 51, // Bitstring 32 bit command
+ // 52-57 reserved
+ IEC104_ASDU_C_SC_TA_1 = 58, // Single command with time tag CP56Time2a
+ IEC104_ASDU_C_DC_TA_1 = 59, // Double command with time tag CP56Time2a
+ IEC104_ASDU_C_RC_TA_1 = 60, // Regulating step command with time tag CP56Time2a
+ IEC104_ASDU_C_SE_TA_1 = 61, // Set-point command with time tag CP56Time2a, normalized value
+ IEC104_ASDU_C_SE_TB_1 = 62, // Set-point command with time tag CP56Time2a, scaled value
+ IEC104_ASDU_C_SE_TC_1 = 63, // Set-point command with time tag CP56Time2a, short floating point number
+ IEC104_ASDU_C_BO_TA_1 = 64, // Bitstring of 32 bit with time tag CP56Time2a
+ // 65-69 reserved
+ IEC104_ASDU_M_EI_NA_1 = 70, // End of initialization
+ // 71-99 reserved
+ IEC104_ASDU_C_IC_NA_1 = 100, // Interrogation command
+ IEC104_ASDU_C_CI_NA_1 = 101, // Counter interrogation command
+ IEC104_ASDU_C_RD_NA_1 = 102, // Read command
+ IEC104_ASDU_C_CS_NA_1 = 103, // Clock synchronization command
+ IEC104_ASDU_C_TS_NA_1 = 104, // Test command
+ IEC104_ASDU_C_RP_NA_1 = 105, // Reset process command
+ IEC104_ASDU_C_CD_NA_1 = 106, // Delay acquisition command
+ IEC104_ASDU_C_TS_TA_1 = 107, // Test command with time tag CP56Time2a
+ // 108-109 reserved
+ IEC104_ASDU_P_ME_NA_1 = 110, // Parameter of measured values, normalized value
+ IEC104_ASDU_P_ME_NB_1 = 111, // Parameter of measured values, scaled value
+ IEC104_ASDU_P_ME_NC_1 = 112, // Parameter of measured values, short floating point number
+ IEC104_ASDU_P_AC_NA_1 = 113, // Parameter activation
+ // 114-119 reserved
+ IEC104_ASDU_F_FR_NA_1 = 120, // File ready
+ IEC104_ASDU_F_SR_NA_1 = 121, // Section ready
+ IEC104_ASDU_F_SC_NA_1 = 122, // Call directory, select file, call file, call section
+ IEC104_ASDU_F_LS_NA_1 = 123, // Last section, last segment
+ IEC104_ASDU_F_AF_NA_1 = 124, // ACK file, ACK section
+ IEC104_ASDU_F_SG_NA_1 = 125, // Single information object
+ IEC104_ASDU_F_DR_TA_1 = 126, // Sequence of information elements in a single information object
+ IEC104_ASDU_F_SC_NB_1 = 127, // QueryLog – Request archive file
+ // 128-256 reserved
+};
+
+// Definition for the different transmission cause codes
+enum CauseOfTransmissionEnum
+{
+ IEC104_CAUSE_TX_NOT_USED = 0, // not used
+ IEC104_CAUSE_TX_PER_CYC = 1, // periodic, cyclic
+ IEC104_CAUSE_TX_BACK = 2, // background scan3
+ IEC104_CAUSE_TX_SPONT = 3, // spontaneous
+ IEC104_CAUSE_TX_INIT = 4, // initialized
+ IEC104_CAUSE_TX_REQ = 5, // request or requested
+ IEC104_CAUSE_TX_ACT = 6, // activation
+ IEC104_CAUSE_TX_ACTCON = 7, // activation confirmation
+ IEC104_CAUSE_TX_DEACT = 8, // deactivation
+ IEC104_CAUSE_TX_DEACTCON = 9, // deactivation confirmation
+ IEC104_CAUSE_TX_ACTTERM = 10, // activation termination
+ IEC104_CAUSE_TX_RETREM = 11, // return information caused by a remote command
+ IEC104_CAUSE_TX_RETLOC = 12, // return information caused by a local command
+ IEC104_CAUSE_TX_FILE = 13, // file transfer
+ IEC104_CAUSE_TX_RES14 = 14, // 14-19 reserved
+ IEC104_CAUSE_TX_RES15 = 15, // 14-19 reserved
+ IEC104_CAUSE_TX_RES16 = 16, // 14-19 reserved
+ IEC104_CAUSE_TX_RES17 = 17, // 14-19 reserved
+ IEC104_CAUSE_TX_RES18 = 18, // 14-19 reserved
+ IEC104_CAUSE_TX_RES19 = 19, // 14-19 reserved
+ IEC104_CAUSE_TX_INROGEN = 20, // interrogated by station interrogation
+ IEC104_CAUSE_TX_INRO1 = 21, // interrogated by group 1 interrogation
+ IEC104_CAUSE_TX_INRO2 = 22, // interrogated by group 2 interrogation
+ IEC104_CAUSE_TX_INRO3 = 23, // interrogated by group 3 interrogation
+ IEC104_CAUSE_TX_INRO4 = 24, // interrogated by group 4 interrogation
+ IEC104_CAUSE_TX_INRO5 = 25, // interrogated by group 5 interrogation
+ IEC104_CAUSE_TX_INRO6 = 26, // interrogated by group 6 interrogation
+ IEC104_CAUSE_TX_INRO7 = 27, // interrogated by group 7 interrogation
+ IEC104_CAUSE_TX_INRO8 = 28, // interrogated by group 8 interrogation
+ IEC104_CAUSE_TX_INRO9 = 29, // interrogated by group 9 interrogation
+ IEC104_CAUSE_TX_INRO10 = 30, // interrogated by group 10 interrogation
+ IEC104_CAUSE_TX_INRO11 = 31, // interrogated by group 11 interrogation
+ IEC104_CAUSE_TX_INRO12 = 32, // interrogated by group 12 interrogation
+ IEC104_CAUSE_TX_INRO13 = 33, // interrogated by group 13 interrogation
+ IEC104_CAUSE_TX_INRO14 = 34, // interrogated by group 14 interrogation
+ IEC104_CAUSE_TX_INRO15 = 35, // interrogated by group 15 interrogation
+ IEC104_CAUSE_TX_INRO16 = 36, // interrogated by group 16 interrogation
+ IEC104_CAUSE_TX_REQCOGEN = 37, // requested by general counter request
+ IEC104_CAUSE_TX_REQCO1 = 38, // requested by group 1 counter request
+ IEC104_CAUSE_TX_REQCO2 = 39, // requested by group 2 counter request
+ IEC104_CAUSE_TX_REQCO3 = 40, // requested by group 3 counter request
+ IEC104_CAUSE_TX_REQCO4 = 41, // requested by group 4 counter request
+ IEC104_CAUSE_TX_RES42 = 42, // 42-43 reserved
+ IEC104_CAUSE_TX_RES43 = 43, // 42-43 reserved
+ IEC104_CAUSE_TX_UNKNOWN_TYPE_ID = 44, // unknown type identification
+ IEC104_CAUSE_TX_UNKNOWN_CAUSE_OF_TX = 45, // unknown cause of transmission
+ IEC104_CAUSE_TX_UNKNOWN_COMMON_ADDR_OF_ASDU = 46, // unknown common address of ASDU
+ IEC104_CAUSE_TX_UNKNOWN_IOA = 47, // unknown information object address
+ IEC104_CAUSE_TX_RES48 = 48, // 48-63 reserved
+ IEC104_CAUSE_TX_RES49 = 49, // 48-63 reserved
+ IEC104_CAUSE_TX_RES50 = 50, // 48-63 reserved
+ IEC104_CAUSE_TX_RES51 = 51, // 48-63 reserved
+ IEC104_CAUSE_TX_RES52 = 52, // 48-63 reserved
+ IEC104_CAUSE_TX_RES53 = 53, // 48-63 reserved
+ IEC104_CAUSE_TX_RES54 = 54, // 48-63 reserved
+ IEC104_CAUSE_TX_RES55 = 55, // 48-63 reserved
+ IEC104_CAUSE_TX_RES56 = 56, // 48-63 reserved
+ IEC104_CAUSE_TX_RES57 = 57, // 48-63 reserved
+ IEC104_CAUSE_TX_RES58 = 58, // 48-63 reserved
+ IEC104_CAUSE_TX_RES59 = 59, // 48-63 reserved
+ IEC104_CAUSE_TX_RES60 = 60, // 48-63 reserved
+ IEC104_CAUSE_TX_RES61 = 61, // 48-63 reserved
+ IEC104_CAUSE_TX_RES62 = 62, // 48-63 reserved
+ IEC104_CAUSE_TX_RES63 = 63, // 48-63 reserved
+};
+
+enum StructureQualifierEnum
+{
+ IEC104_SQ_FALSE = 0,
+ IEC104_SQ_TRUE = 1,
+};
+
+
+
+
+//
+//
+// Structs
+//
+//
+
+//
+// Generic structs
+//
+
+// struct Iec104To help determine what type of APCI is in use
+struct Iec104GenericApci
+{
+ uint8_t start;
+ uint8_t length;
+ uint8_t apciTypeMajor : 1;
+ uint8_t apciTypeMinor : 1;
+ uint8_t reserved : 6;
+}__attribute__((packed));
+
+
+//
+// ASDU Information Object Structs
+//
+
+struct Iec104VariableStructureQualifierType
+{
+ uint8_t numberOfElements : 7;
+ uint8_t sq : 1;
+}__attribute__((packed));
+
+// This structure does not require the OA, but it seems to be used in all traffic seen so far
+struct Iec104CauseOfTransmissionType
+{
+ uint8_t causeOfTransmission : 6;
+ uint8_t pn : 1;
+ uint8_t test : 1;
+ uint8_t oa;
+}__attribute__((packed));
+
+// COI: Cause of Initialization Structure
+struct Iec104CoiType
+{
+ uint8_t ui : 7;
+ uint8_t bs : 1;
+}__attribute__((packed));
+
+// QOI: Qualifier of Interrogation Structure
+struct Iec104QoiType
+{
+ uint8_t qoi;
+}__attribute__((packed));
+
+// QCC: Qualifier of Counter Interrogation Command Structure
+struct Iec104QccType
+{
+ uint8_t rqt : 6;
+ uint8_t frz : 2;
+}__attribute__((packed));
+
+// QPM: Qualifier of Parameter of Measured Values Structure
+struct Iec104QpmType
+{
+ uint8_t kpa : 6;
+ uint8_t lpc : 1;
+ uint8_t pop : 1;
+}__attribute__((packed));
+
+// QPA: Qualifier of Parameter Activation Structure
+struct Iec104QpaType
+{
+ uint8_t qpa;
+}__attribute__((packed));
+
+// QOC: Qualifier of Command Structure
+// This doesn't add up to 8, but that is expected
+// This struct gets used in fields that have 2 preceding bits
+struct Iec104QocType
+{
+ uint8_t qu : 5;
+ uint8_t se : 1;
+}__attribute__((packed));
+
+// QRP: Qualifier of Reset Process Structure
+struct Iec104QrpType
+{
+ uint8_t qrp;
+}__attribute__((packed));
+
+// FRQ: File Ready Qualifier Structure
+struct Iec104FrqType
+{
+ uint8_t ui : 7;
+ uint8_t bs : 1;
+}__attribute__((packed));
+
+// SRQ: Section Ready Qualifier Structure
+struct Iec104SrqType
+{
+ uint8_t ui : 7;
+ uint8_t bs : 1;
+}__attribute__((packed));
+
+// SCQ: Select and Call Qualifier Structure
+struct Iec104ScqType
+{
+ uint8_t ui1 : 4;
+ uint8_t ui2 : 4;
+}__attribute__((packed));
+
+// LSQ: Last Section or Segment Qualifier Structure
+struct Iec104LsqType
+{
+ uint8_t lsq;
+}__attribute__((packed));
+
+// AFQ: Acknowledge File or Section Qualifier Structure
+struct Iec104AfqType
+{
+ uint8_t ui1 : 4;
+ uint8_t ui2 : 4;
+}__attribute__((packed));
+
+// Common Address of ASDU Structure
+// This structure does not require the high octet, but it seems to be
+// used in all traffic seen so far
+struct Iec104CommonAddressOfAsduType
+{
+ uint16_t commonAddress;
+}__attribute__((packed));
+
+// Information Object Address One Octet Structure
+struct Iec104InformationObjectAddressOneOctetType
+{
+ uint8_t informationObjectAddress;
+}__attribute__((packed));
+
+// Information Object Address Two Octet Structure
+struct Iec104InformationObjectAddressTwoOctetType
+{
+ uint8_t informationObjectAddress[2];
+}__attribute__((packed));
+
+// Information Object Address Three Octet Structure
+struct Iec104InformationObjectAddressThreeOctetType
+{
+ uint8_t informationObjectAddress[3];
+}__attribute__((packed));
+
+// SIQ: Single Point Information with Quality Descriptor Structure
+struct Iec104SiqType
+{
+ uint8_t spi : 1;
+ uint8_t reserved : 3;
+ uint8_t bl : 1;
+ uint8_t sb : 1;
+ uint8_t nt : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// DIQ: Double Point Information with Quality Descriptor Structure
+struct Iec104DiqType
+{
+ uint8_t dpi : 2;
+ uint8_t reserved : 2;
+ uint8_t bl : 1;
+ uint8_t sb : 1;
+ uint8_t nt : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// QDS: Quality Descriptor Structure
+struct Iec104QdsType
+{
+ uint8_t ov : 1;
+ uint8_t reserved : 3;
+ uint8_t bl : 1;
+ uint8_t sb : 1;
+ uint8_t nt : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// QDP: Quality Descriptor for Events of Protection Equipment Structure
+struct Iec104QdpType
+{
+ uint8_t reserved : 3;
+ uint8_t ei : 1;
+ uint8_t bl : 1;
+ uint8_t sb : 1;
+ uint8_t nt : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// VTI: Value with Transient State Indication Structure
+struct Iec104VtiType
+{
+ uint8_t value : 7;
+ uint8_t t : 1;
+}__attribute__((packed));
+
+// NVA: Normalized Value Structure
+struct Iec104NvaType
+{
+ uint16_t value;
+}__attribute__((packed));
+
+// SVA: Scaled Value Structure
+struct Iec104SvaType
+{
+ uint16_t value;
+}__attribute__((packed));
+
+// IEEE_STD_754: Short Floating Point Number Structure
+struct Iec104IeeeStd754Type
+{
+ uint32_t ieeeStd754;
+}__attribute__((packed));
+
+// BCR: Binary Counter Reading Structure
+struct Iec104BcrType
+{
+ uint32_t value;
+ uint8_t sequenceNumber : 5;
+ uint8_t cy : 1;
+ uint8_t ca : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// SEP: Single Event of Protection Equipment Structure
+struct Iec104SepType
+{
+ uint8_t es : 2;
+ uint8_t reserved : 1;
+ uint8_t ei : 1;
+ uint8_t bl : 1;
+ uint8_t sb : 1;
+ uint8_t nt : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// SPE: Start Event of Protection Equipment Structure
+struct Iec104SpeType
+{
+ uint8_t gs : 1;
+ uint8_t sl1 : 1;
+ uint8_t sl2 : 1;
+ uint8_t sl3 : 1;
+ uint8_t sie : 1;
+ uint8_t srd : 1;
+ uint8_t reserved : 2;
+}__attribute__((packed));
+
+// OCI: Output Circuit Information Structure
+struct Iec104OciType
+{
+ uint8_t gc : 1;
+ uint8_t cl1 : 1;
+ uint8_t cl2 : 1;
+ uint8_t cl3 : 1;
+ uint8_t reserved : 4;
+}__attribute__((packed));
+
+// BSI: Binary State Information Structure
+struct Iec104BsiType
+{
+ uint32_t bitstring;
+}__attribute__((packed));
+
+// FBP: Fixed Test Bit Pattern Structure
+struct Iec104FbpType
+{
+ uint16_t fixedTestBitPattern;
+}__attribute__((packed));
+
+// SCO: Single Command Structure
+struct Iec104ScoType
+{
+ uint8_t scs : 1;
+ uint8_t reserved : 1;
+ uint8_t qu : 5;
+ uint8_t se : 1;
+}__attribute__((packed));
+
+// DCO: Double Command Structure
+struct Iec104DcoType
+{
+ uint8_t dcs : 2;
+ uint8_t qu : 5;
+ uint8_t se : 1;
+}__attribute__((packed));
+
+// RCO: Regulating Step Command Structure
+struct Iec104RcoType
+{
+ uint8_t rcs : 2;
+ uint8_t qu : 5;
+ uint8_t se : 1;
+}__attribute__((packed));
+
+// Time2a Milliseconds Structure
+struct Iec104Time2aMillisecondsType
+{
+ uint16_t milliseconds;
+}__attribute__((packed));
+
+// Time2a IVResMinute Structure
+struct Iec104Time2aIvresminuteType
+{
+ uint8_t minutes : 6;
+ uint8_t res : 1;
+ uint8_t iv : 1;
+}__attribute__((packed));
+
+// Time2a SURes2Hour Structure
+struct Iec104Time2aSures2hourType
+{
+ uint8_t hours : 5;
+ uint8_t res2 : 2;
+ uint8_t su : 1;
+}__attribute__((packed));
+
+// Time2a DOWDay Structure
+struct Iec104Time2aDowdayType
+{
+ uint8_t dayOfMonth : 5;
+ uint8_t dayOfWeek : 3;
+}__attribute__((packed));
+
+// Time2a Res3Month Structure
+struct Iec104Time2aRes3monthType
+{
+ uint8_t month : 4;
+ uint8_t res3 : 4;
+}__attribute__((packed));
+
+// Time2a Res4Year Structure
+struct Iec104Time2aRes4yearType
+{
+ uint8_t year : 7;
+ uint8_t res4 : 1;
+}__attribute__((packed));
+
+// CP56Time2a Structure
+struct Iec104Cp56Time2aType
+{
+ Iec104Time2aMillisecondsType milliseconds;
+ Iec104Time2aIvresminuteType ivresminute;
+ Iec104Time2aSures2hourType sures2hour;
+ Iec104Time2aDowdayType dowday;
+ Iec104Time2aRes3monthType res3month;
+ Iec104Time2aRes4yearType res4year;
+}__attribute__((packed));
+
+// Cp24Time2a Structure
+struct Iec104Cp24Time2aType
+{
+ Iec104Time2aMillisecondsType milliseconds;
+ Iec104Time2aIvresminuteType ivresminute;
+}__attribute__((packed));
+
+// Cp16Time2a Structure
+struct Iec104Cp16Time2aType
+{
+ Iec104Time2aMillisecondsType milliseconds;
+}__attribute__((packed));
+
+// NOF: Name of File Structure
+struct Iec104NofType
+{
+ uint16_t nameOfFile;
+}__attribute__((packed));
+
+// NOS: Name of Section Structure
+struct Iec104NosType
+{
+ uint8_t nameOfSection;
+}__attribute__((packed));
+
+// LOF: Length of File or Section Structure
+struct Iec104LofType
+{
+ uint8_t lengthOfFile[3];
+}__attribute__((packed));
+
+// LOS: Length of Segment Structure
+struct Iec104LosType
+{
+ uint8_t lengthOfSegment;
+}__attribute__((packed));
+
+// CHS: Checksum Structure
+struct Iec104ChsType
+{
+ uint8_t chs;
+}__attribute__((packed));
+
+// SOF: Status of File Structure
+// need to prepend `sof` tag on here since `for` is a reserved word
+// doing it for the rest for consistency
+struct Iec104SofType
+{
+ uint8_t sofStatus : 5;
+ uint8_t sofLfd : 1;
+ uint8_t sofFor : 1;
+ uint8_t sofFa : 1;
+}__attribute__((packed));
+
+// QOS: Qualifier of Set Point Command Structure
+struct Iec104QosType
+{
+ uint8_t ql : 7;
+ uint8_t se : 1;
+}__attribute__((packed));
+
+// SCD: Status + Status Change Detection Structure
+struct Iec104ScdType
+{
+ uint16_t st;
+ uint16_t cd;
+}__attribute__((packed));
+
+// TSC: Test Sequence Counter
+struct Iec104TscType
+{
+ uint16_t tsc;
+}__attribute__((packed));
+
+// Segment: Segment type
+struct Iec104SegmentType
+{
+ uint8_t segment;
+}__attribute__((packed));
+
+// Information Element
+struct Iec104InformationElementType
+{
+ Iec104NofType nameOfFileOrSubdirectory;
+ Iec104LofType lengthOfFile;
+ Iec104SofType sof;
+ Iec104Cp56Time2aType creationTimeOfFile;
+}__attribute__((packed));
+
+
+//
+//
+// ASDU structs
+//
+//
+
+//
+// ASDUs for process information in monitor direction
+//
+
+// ASDU Type M_SP_NA_1
+// Ident 1
+// Single-point information
+
+struct Iec104M_SP_NA_1_IO_Subgroup
+{
+ Iec104SiqType siq;
+}__attribute__((packed));
+
+struct Iec104M_SP_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_SP_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_SP_TA_1
+// Ident 2
+// Single-point information with time tag
+
+struct Iec104M_SP_TA_1_IO_Subgroup
+{
+ Iec104SiqType siq;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_SP_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_SP_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_DP_NA_1
+// Ident 3
+// Double-point information
+
+struct Iec104M_DP_NA_1_IO_Subgroup
+{
+ Iec104DiqType diq;
+}__attribute__((packed));
+
+struct Iec104M_DP_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_DP_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_DP_TA_1
+// Ident 4
+// Double-point information with time tag
+
+struct Iec104M_DP_TA_1_IO_Subgroup
+{
+ Iec104DiqType diq;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_DP_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_DP_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ST_NA_1
+// Ident 5
+// Step position information
+
+struct Iec104M_ST_NA_1_IO_Subgroup
+{
+ Iec104VtiType vti;
+ Iec104QdsType qds;
+}__attribute__((packed));
+
+struct Iec104M_ST_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ST_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ST_TA_1
+// Ident 6
+// Step position information with time tag
+
+struct Iec104M_ST_TA_1_IO_Subgroup
+{
+ Iec104VtiType vti;
+ Iec104QdsType qds;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ST_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ST_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_BO_NA_1
+// Ident 7
+// Bitstring of 32 bit
+
+struct Iec104M_BO_NA_1_IO_Subgroup
+{
+ Iec104BsiType bsi;
+ Iec104QdsType qds;
+}__attribute__((packed));
+
+struct Iec104M_BO_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_BO_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_BO_TA_1
+// Ident 8
+// Bitstring of 32 bit with time tag
+
+struct Iec104M_BO_TA_1_IO_Subgroup
+{
+ Iec104BsiType bsi;
+ Iec104QdsType qds;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_BO_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_BO_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_NA_1
+// Ident 9
+// Measured value, normalized value
+
+struct Iec104M_ME_NA_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+ Iec104QdsType qds;
+}__attribute__((packed));
+
+struct Iec104M_ME_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_TA_1
+// Ident 10
+// Measured value, normalized value with time tag
+
+struct Iec104M_ME_TA_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+ Iec104QdsType qds;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ME_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_NB_1
+// Ident 11
+// Measured value, scaled value
+
+struct Iec104M_ME_NB_1_IO_Subgroup
+{
+ Iec104SvaType sva;
+ Iec104QdsType qds;
+}__attribute__((packed));
+
+struct Iec104M_ME_NB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_NB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_TB_1
+// Ident 12
+// Measured value, scaled value wit time tag
+
+struct Iec104M_ME_TB_1_IO_Subgroup
+{
+ Iec104SvaType sva;
+ Iec104QdsType qds;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ME_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_NC_1
+// Ident 13
+// Measured value, short floating point number
+
+struct Iec104M_ME_NC_1_IO_Subgroup
+{
+ Iec104IeeeStd754Type ieeeStd754;
+ Iec104QdsType qds;
+}__attribute__((packed));
+
+struct Iec104M_ME_NC_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_NC_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_TC_1
+// Ident 14
+// Measured value, short floating point number with time tag
+
+struct Iec104M_ME_TC_1_IO_Subgroup
+{
+ Iec104IeeeStd754Type ieeeStd754;
+ Iec104QdsType qds;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ME_TC_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_TC_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_IT_NA_1
+// Ident 15
+// Integrated totals
+
+struct Iec104M_IT_NA_1_IO_Subgroup
+{
+ Iec104BcrType bcr;
+}__attribute__((packed));
+
+struct Iec104M_IT_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_IT_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_IT_TA_1
+// Ident 16
+// Integrated totals with time tag
+
+struct Iec104M_IT_TA_1_IO_Subgroup
+{
+ Iec104BcrType bcr;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_IT_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_IT_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_EP_TA_1
+// Ident 17
+// Event of protection equipment with time tag
+
+struct Iec104M_EP_TA_1_IO_Subgroup
+{
+ Iec104SepType sep;
+ Iec104Cp16Time2aType elapsedTime;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_EP_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EP_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_EP_TB_1
+// Ident 18
+// Packed start events of protection equipment with time tag
+
+struct Iec104M_EP_TB_1_IO_Subgroup
+{
+ Iec104SpeType spe;
+ Iec104QdpType qdp;
+ Iec104Cp16Time2aType relayDurationTime;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_EP_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EP_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_EP_TC_1
+// Ident 19
+// Packed output circuit information of protection equipment with time tag
+
+struct Iec104M_EP_TC_1_IO_Subgroup
+{
+ Iec104OciType oci;
+ Iec104QdpType qdp;
+ Iec104Cp16Time2aType relayOperatingTime;
+ Iec104Cp24Time2aType threeOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_EP_TC_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EP_TC_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_PS_NA_1
+// Ident 20
+// Packed single point information with status change detection
+
+struct Iec104M_PS_NA_1_IO_Subgroup
+{
+ Iec104ScdType scd;
+ Iec104QdsType qds;
+}__attribute__((packed));
+
+struct Iec104M_PS_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_PS_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_ND_1
+// Ident 21
+// Measured value, normalized value without quality descriptor
+
+struct Iec104M_ME_ND_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+}__attribute__((packed));
+
+struct Iec104M_ME_ND_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_ND_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_SP_TB_1
+// Ident 30
+// Single-point information with time tag CP56Time2a
+
+struct Iec104M_SP_TB_1_IO_Subgroup
+{
+ Iec104SiqType siq;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_SP_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_SP_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_DP_TB_1
+// Ident 31
+// Double-point information with time tag CP56Time2a
+
+struct Iec104M_DP_TB_1_IO_Subgroup
+{
+ Iec104DiqType diq;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_DP_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_DP_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ST_TB_1
+// Ident 32
+// Step position information with time tag CP56Time2a
+
+struct Iec104M_ST_TB_1_IO_Subgroup
+{
+ Iec104VtiType vti;
+ Iec104QdsType qds;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ST_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ST_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_BO_TB_1
+// Ident 33
+// Bitstring of 32 bit with time tag CP56Time2a
+
+struct Iec104M_BO_TB_1_IO_Subgroup
+{
+ Iec104BsiType bsi;
+ Iec104QdsType qds;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_BO_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_BO_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_TD_1
+// Ident 34
+// Measured value, normalized value with time tag CP56Time2a
+
+struct Iec104M_ME_TD_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+ Iec104QdsType qds;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ME_TD_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_TD_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_TE_1
+// Ident 35
+// Measured value, scaled value with time tag CP56Time2a
+
+struct Iec104M_ME_TE_1_IO_Subgroup
+{
+ Iec104SvaType sva;
+ Iec104QdsType qds;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ME_TE_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_TE_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_ME_TF_1
+// Ident 36
+// Measured value, short floating point number with time tag CP56Time2a
+
+struct Iec104M_ME_TF_1_IO_Subgroup
+{
+ Iec104IeeeStd754Type ieeeStd754;
+ Iec104QdsType qds;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_ME_TF_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_ME_TF_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_IT_TB_1
+// Ident 37
+// Integrated totals with time tag CP56Time2a
+
+struct Iec104M_IT_TB_1_IO_Subgroup
+{
+ Iec104BcrType bcr;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_IT_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_IT_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_EP_TD_1
+// Ident 38
+// Event of protection equipment with time tag CP56Time2a
+
+struct Iec104M_EP_TD_1_IO_Subgroup
+{
+ Iec104SepType sep;
+ Iec104Cp16Time2aType elapsedTime;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_EP_TD_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EP_TD_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_EP_TE_1
+// Ident 39
+// Packed start events of protection equipment with time tag CP56Time2a
+
+struct Iec104M_EP_TE_1_IO_Subgroup
+{
+ Iec104SepType sep;
+ Iec104QdpType qdp;
+ Iec104Cp16Time2aType relayDurationTime;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_EP_TE_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EP_TE_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type M_EP_TF_1
+// Ident 40
+// Packed output circuit information of protection equipment with time tag CP56Time2a
+
+struct Iec104M_EP_TF_1_IO_Subgroup
+{
+ Iec104OciType oci;
+ Iec104QdpType qdp;
+ Iec104Cp16Time2aType relayDurationTime;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104M_EP_TF_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EP_TF_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+
+//
+// ASDUs for process information in control direction
+//
+
+// ASDU Type C_SC_NA_1
+
+struct Iec104C_SC_NA_1_IO_Subgroup
+{
+ Iec104ScoType sco;
+}__attribute__((packed));
+
+struct Iec104C_SC_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SC_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_DC_NA_1
+
+struct Iec104C_DC_NA_1_IO_Subgroup
+{
+ Iec104DcoType dco;
+}__attribute__((packed));
+
+struct Iec104C_DC_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_DC_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_RC_NA_1
+
+struct Iec104C_RC_NA_1_IO_Subgroup
+{
+ Iec104RcoType rco;
+}__attribute__((packed));
+
+struct Iec104C_RC_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_RC_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SE_NA_1
+
+struct Iec104C_SE_NA_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+ Iec104QosType qos;
+}__attribute__((packed));
+
+struct Iec104C_SE_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SE_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SE_NB_1
+
+struct Iec104C_SE_NB_1_IO_Subgroup
+{
+ Iec104SvaType sva;
+ Iec104QosType qos;
+}__attribute__((packed));
+
+struct Iec104C_SE_NB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SE_NB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SE_NC_1
+
+struct Iec104C_SE_NC_1_IO_Subgroup
+{
+ Iec104IeeeStd754Type ieeeStd754;
+ Iec104QosType qos;
+}__attribute__((packed));
+
+struct Iec104C_SE_NC_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SE_NC_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_BO_NA_1
+
+struct Iec104C_BO_NA_1_IO_Subgroup
+{
+ Iec104BsiType bsi;
+}__attribute__((packed));
+
+struct Iec104C_BO_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_BO_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SC_TA_1
+// Ident 58
+// Single command with time tag CP56Time2a
+// IEC-60870-5-104
+
+struct Iec104C_SC_TA_1_IO_Subgroup
+{
+ Iec104ScoType sco;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_SC_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SC_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_DC_TA_1
+// Ident 59
+// Double command with time tag CP56Time2a
+// IEC-60870-5-104
+
+struct Iec104C_DC_TA_1_IO_Subgroup
+{
+ Iec104DcoType dco;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_DC_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_DC_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_RC_TA_1
+// Ident 60
+// Regulating step command with time tag CP56Time2a
+// IEC-60870-5-104
+
+struct Iec104C_RC_TA_1_IO_Subgroup
+{
+ Iec104RcoType rco;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_RC_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_RC_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SE_TA_1
+// Ident 61
+// Set-point command with time tag CP56Time2a, normalized value
+// IEC-60870-5-104
+
+struct Iec104C_SE_TA_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+ Iec104QosType qos;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_SE_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SE_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SE_TB_1
+// Ident 62
+// Set-point command with time tag CP56Time2a, scaled value
+// IEC-60870-5-104
+
+struct Iec104C_SE_TB_1_IO_Subgroup
+{
+ Iec104SvaType sva;
+ Iec104QosType qos;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_SE_TB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SE_TB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_SE_TC_1
+// Ident 63
+// Set-point command with time tag CP56Time2a, short floating point number
+// IEC-60870-5-104
+
+struct Iec104C_SE_TC_1_IO_Subgroup
+{
+ Iec104IeeeStd754Type ieeeStd754;
+ Iec104QosType qos;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_SE_TC_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_SE_TC_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_BO_TA_1
+// Ident 64
+// Bitstring of 32 bit with time tag CP56Time2a
+// IEC-60870-5-104
+
+struct Iec104C_BO_TA_1_IO_Subgroup
+{
+ Iec104BsiType bsi;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_BO_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_BO_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+
+//
+// ASDUs for system information in monitor direction
+//
+
+// ASDU Type M_EI_NA_1
+// Ident 70
+// End of initialization
+
+struct Iec104M_EI_NA_1_IO_Subgroup
+{
+ Iec104CoiType coi;
+}__attribute__((packed));
+
+struct Iec104M_EI_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104M_EI_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+
+//
+// ASDUs for system information in control direction
+//
+
+// ASDU Type C_IC_NA_1
+// Ident 100
+// Interrogation command
+
+struct Iec104C_IC_NA_1_IO_Subgroup
+{
+ Iec104QoiType qoi;
+}__attribute__((packed));
+
+struct Iec104C_IC_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_IC_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_CI_NA_1
+// Ident 101
+// Counter interrogation command
+
+struct Iec104C_CI_NA_1_IO_Subgroup
+{
+ Iec104QccType qcc;
+}__attribute__((packed));
+
+struct Iec104C_CI_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_CI_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_RD_NA_1
+// Ident 102
+// Read command
+
+struct Iec104C_RD_NA_1_IO_Subgroup
+{
+ // No subgroup for this type
+}__attribute__((packed));
+
+struct Iec104C_RD_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_RD_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_CS_NA_1
+// Ident 103
+// Clock synchronization command
+
+struct Iec104C_CS_NA_1_IO_Subgroup
+{
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_CS_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_CS_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_TS_NA_1
+// Ident 104
+// Test command
+
+struct Iec104C_TS_NA_1_IO_Subgroup
+{
+ Iec104FbpType fbp;
+}__attribute__((packed));
+
+struct Iec104C_TS_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_TS_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_RP_NA_1
+// Ident 105
+// Reset process command
+
+struct Iec104C_RP_NA_1_IO_Subgroup
+{
+ Iec104QrpType qrp;
+}__attribute__((packed));
+
+struct Iec104C_RP_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_RP_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_CD_NA_1
+// Ident 106
+// Delay acquisition command
+
+struct Iec104C_CD_NA_1_IO_Subgroup
+{
+ Iec104Cp16Time2aType msUpToSeconds;
+}__attribute__((packed));
+
+struct Iec104C_CD_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_CD_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type C_TS_TA_1
+// Ident 107
+// Test command with time tag CP56Time2a
+// IEC-60870-5-104
+
+struct Iec104C_TS_TA_1_IO_Subgroup
+{
+ Iec104TscType tsc;
+ Iec104Cp56Time2aType sevenOctetBinaryTime;
+}__attribute__((packed));
+
+struct Iec104C_TS_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104C_TS_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+
+//
+// ASDUs for parameter in control direction
+//
+
+// ASDU Type P_ME_NA_1
+// Ident 110
+// Parameter of measured values, normalized value
+
+struct Iec104P_ME_NA_1_IO_Subgroup
+{
+ Iec104NvaType nva;
+ Iec104QpmType qpm;
+}__attribute__((packed));
+
+struct Iec104P_ME_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104P_ME_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type P_ME_NB_1
+// Ident 111
+// Parameter of measured values, scaled value
+
+struct Iec104P_ME_NB_1_IO_Subgroup
+{
+ Iec104SvaType sva;
+ Iec104QpmType qpm;
+}__attribute__((packed));
+
+struct Iec104P_ME_NB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104P_ME_NB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type P_ME_NC_1
+// Ident 112
+// Parameter of measured values, short floating point number
+
+struct Iec104P_ME_NC_1_IO_Subgroup
+{
+ Iec104IeeeStd754Type ieeeStd754;
+ Iec104QpmType qpm;
+}__attribute__((packed));
+
+struct Iec104P_ME_NC_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104P_ME_NC_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type P_AC_NA_1
+// Ident 113
+// Parameter activation
+
+struct Iec104P_AC_NA_1_IO_Subgroup
+{
+ Iec104QpaType qpa;
+}__attribute__((packed));
+
+struct Iec104P_AC_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104P_AC_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+
+//
+// ASDUs for file transfer
+//
+
+// ASDU Type F_FR_NA_1
+// Ident 120
+// File ready
+
+struct Iec104F_FR_NA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104LofType lengthOfFile;
+ Iec104FrqType frq;
+}__attribute__((packed));
+
+struct Iec104F_FR_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_FR_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_SR_NA_1
+// Ident 121
+// Section ready
+
+struct Iec104F_SR_NA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104NosType nameOfSection;
+ Iec104LofType lengthOfFileOrSection;
+ Iec104SrqType srq;
+}__attribute__((packed));
+
+struct Iec104F_SR_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_SR_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_SC_NA_1
+// Ident 122
+// Call directory, select file, call file, call section
+
+struct Iec104F_SC_NA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104NosType nameOfSection;
+ Iec104ScqType scq;
+}__attribute__((packed));
+
+struct Iec104F_SC_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_SC_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_LS_NA_1
+// Ident 123
+// Last section, last segment
+
+struct Iec104F_LS_NA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104NosType nameOfSection;
+ Iec104LsqType lsq;
+ Iec104ChsType chs;
+}__attribute__((packed));
+
+struct Iec104F_LS_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_LS_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_AF_NA_1
+// Ident 124
+// ACK file, ACK section
+
+struct Iec104F_AF_NA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104NosType nameOfSection;
+ Iec104AfqType afq;
+}__attribute__((packed));
+
+struct Iec104F_AF_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_AF_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_SG_NA_1
+// Ident 125
+// Single information object
+
+struct Iec104F_SG_NA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104NosType nameOfSection;
+ Iec104LosType lengthOfSegment;
+ Iec104SegmentType segment;
+}__attribute__((packed));
+
+struct Iec104F_SG_NA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_SG_NA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_DR_TA_1
+// Ident 126
+// Sequence of information elements in a single information object
+
+struct Iec104F_DR_TA_1_IO_Subgroup
+{
+ Iec104NofType nameOfFileOrSubdirectory;
+ Iec104LofType lengthOfFile;
+ Iec104SofType sof;
+ Iec104Cp56Time2aType creationTimeOfFile;
+}__attribute__((packed));
+
+struct Iec104F_DR_TA_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_DR_TA_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+// ASDU Type F_SC_NB_1
+// Ident 127
+// QueryLog – Request archive file
+
+struct Iec104F_SC_NB_1_IO_Subgroup
+{
+ Iec104NofType nameOfFile;
+ Iec104Cp56Time2aType startTime;
+ Iec104Cp56Time2aType stopTime;
+}__attribute__((packed));
+
+struct Iec104F_SC_NB_1_IO_Group
+{
+ Iec104InformationObjectAddressThreeOctetType ioa;
+ Iec104F_SC_NB_1_IO_Subgroup subgroup;
+}__attribute__((packed));
+
+
+//
+// Generic ASDU
+//
+
+struct Iec104GenericAsdu
+{
+ uint8_t typeId;
+ Iec104VariableStructureQualifierType variableStructureQualifier;
+ Iec104CauseOfTransmissionType causeOfTransmission;
+ Iec104CommonAddressOfAsduType commonAddressOfAsdu;
+ union
+ {
+ Iec104M_SP_NA_1_IO_Group m_sp_na_1;
+ Iec104M_SP_TA_1_IO_Group m_sp_ta_1;
+ Iec104M_DP_NA_1_IO_Group m_dp_na_1;
+ Iec104M_DP_TA_1_IO_Group m_dp_ta_1;
+ Iec104M_ST_NA_1_IO_Group m_st_na_1;
+ Iec104M_ST_TA_1_IO_Group m_st_ta_1;
+ Iec104M_BO_NA_1_IO_Group m_bo_na_1;
+ Iec104M_BO_TA_1_IO_Group m_bo_ta_1;
+ Iec104M_ME_NA_1_IO_Group m_me_na_1;
+ Iec104M_ME_TA_1_IO_Group m_me_ta_1;
+ Iec104M_ME_NB_1_IO_Group m_me_nb_1;
+ Iec104M_ME_TB_1_IO_Group m_me_tb_1;
+ Iec104M_ME_NC_1_IO_Group m_me_nc_1;
+ Iec104M_ME_TC_1_IO_Group m_me_tc_1;
+ Iec104M_IT_NA_1_IO_Group m_it_na_1;
+ Iec104M_IT_TA_1_IO_Group m_it_ta_1;
+ Iec104M_EP_TA_1_IO_Group m_ep_ta_1;
+ Iec104M_EP_TB_1_IO_Group m_ep_tb_1;
+ Iec104M_EP_TC_1_IO_Group m_ep_tc_1;
+ Iec104M_PS_NA_1_IO_Group m_ps_na_1;
+ Iec104M_ME_ND_1_IO_Group m_me_nd_1;
+ Iec104M_SP_TB_1_IO_Group m_sp_tb_1;
+ Iec104M_DP_TB_1_IO_Group m_dp_tb_1;
+ Iec104M_ST_TB_1_IO_Group m_st_tb_1;
+ Iec104M_BO_TB_1_IO_Group m_bo_tb_1;
+ Iec104M_ME_TD_1_IO_Group m_me_td_1;
+ Iec104M_ME_TE_1_IO_Group m_me_te_1;
+ Iec104M_ME_TF_1_IO_Group m_me_tf_1;
+ Iec104M_IT_TB_1_IO_Group m_it_tb_1;
+ Iec104M_EP_TD_1_IO_Group m_ep_td_1;
+ Iec104M_EP_TE_1_IO_Group m_ep_te_1;
+ Iec104M_EP_TF_1_IO_Group m_ep_tf_1;
+ Iec104C_SC_NA_1_IO_Group c_sc_na_1;
+ Iec104C_DC_NA_1_IO_Group c_dc_na_1;
+ Iec104C_RC_NA_1_IO_Group c_rc_na_1;
+ Iec104C_SE_NA_1_IO_Group c_se_na_1;
+ Iec104C_SE_NB_1_IO_Group c_se_nb_1;
+ Iec104C_SE_NC_1_IO_Group c_se_nc_1;
+ Iec104C_BO_NA_1_IO_Group c_bo_na_1;
+ Iec104C_SC_TA_1_IO_Group c_sc_ta_1;
+ Iec104C_DC_TA_1_IO_Group c_dc_ta_1;
+ Iec104C_RC_TA_1_IO_Group c_rc_ta_1;
+ Iec104C_SE_TA_1_IO_Group c_se_ta_1;
+ Iec104C_SE_TB_1_IO_Group c_se_tb_1;
+ Iec104C_SE_TC_1_IO_Group c_se_tc_1;
+ Iec104C_BO_TA_1_IO_Group c_bo_ta_1;
+ Iec104M_EI_NA_1_IO_Group m_ei_na_1;
+ Iec104C_IC_NA_1_IO_Group c_ic_na_1;
+ Iec104C_CI_NA_1_IO_Group c_ci_na_1;
+ Iec104C_RD_NA_1_IO_Group c_rd_na_1;
+ Iec104C_CS_NA_1_IO_Group c_cs_na_1;
+ Iec104C_TS_NA_1_IO_Group c_ts_na_1;
+ Iec104C_RP_NA_1_IO_Group c_rp_na_1;
+ Iec104C_CD_NA_1_IO_Group c_cd_na_1;
+ Iec104C_TS_TA_1_IO_Group c_ts_ta_1;
+ Iec104P_ME_NA_1_IO_Group p_me_na_1;
+ Iec104P_ME_NB_1_IO_Group p_me_nb_1;
+ Iec104P_ME_NC_1_IO_Group p_me_nc_1;
+ Iec104P_AC_NA_1_IO_Group p_ac_na_1;
+ Iec104F_FR_NA_1_IO_Group f_fr_na_1;
+ Iec104F_SR_NA_1_IO_Group f_sr_na_1;
+ Iec104F_SC_NA_1_IO_Group f_sc_na_1;
+ Iec104F_LS_NA_1_IO_Group f_ls_na_1;
+ Iec104F_AF_NA_1_IO_Group f_af_na_1;
+ Iec104F_SG_NA_1_IO_Group f_sg_na_1;
+ Iec104F_DR_TA_1_IO_Group f_dr_ta_1;
+ Iec104F_SC_NB_1_IO_Group f_sc_nb_1;
+ };
+}__attribute__((packed));
+
+
+//
+// APCI structs
+//
+
+// Header fields common to every APCI
+struct Iec104Header
+{
+ uint8_t start;
+ uint8_t length;
+}__attribute__((packed));
+
+// APCI Type U
+struct Iec104ApciU
+{
+ Iec104Header header;
+ uint8_t apciTypeMajor : 1;
+ uint8_t apciTypeMinor : 1;
+ uint8_t startdtAct : 1;
+ uint8_t startdtCon : 1;
+ uint8_t stopdtAct : 1;
+ uint8_t stopdtCon : 1;
+ uint8_t testfrAct : 1;
+ uint8_t testfrCon : 1;
+ uint8_t reserved1;
+ uint16_t reserved2 : 1;
+ uint16_t reserved3 : 15;
+}__attribute__((packed));
+
+// APCI Type S
+struct Iec104ApciS
+{
+ Iec104Header header;
+ uint16_t apciTypeMajor : 1;
+ uint16_t apciTypeMinor : 1;
+ uint16_t reserved1 : 14;
+ uint16_t reserved2 : 1;
+ uint16_t recvSeq : 15;
+}__attribute__((packed));
+
+// APCI Type I
+struct Iec104ApciI
+{
+ Iec104Header header;
+ uint16_t apciTypeMajor : 1;
+ uint16_t sendSeq : 15;
+ uint16_t reserved : 1;
+ uint16_t recvSeq : 15;
+ Iec104GenericAsdu asdu;
+}__attribute__((packed));
+
+// structs used to determine if there is an issue with the passed ASDU
+struct Iec104AsduCheckCauseOfTx
+{
+ uint64_t percyc : 1;
+ uint64_t back : 1;
+ uint64_t spont : 1;
+ uint64_t init : 1;
+ uint64_t req : 1;
+ uint64_t act : 1;
+ uint64_t actcon : 1;
+ uint64_t deact : 1;
+ uint64_t deactcon : 1;
+ uint64_t actterm : 1;
+ uint64_t retrem : 1;
+ uint64_t retloc : 1;
+ uint64_t file : 1;
+ uint64_t inrogen : 1;
+ uint64_t inro1 : 1;
+ uint64_t inro2 : 1;
+ uint64_t inro3 : 1;
+ uint64_t inro4 : 1;
+ uint64_t inro5 : 1;
+ uint64_t inro6 : 1;
+ uint64_t inro7 : 1;
+ uint64_t inro8 : 1;
+ uint64_t inro9 : 1;
+ uint64_t inro10 : 1;
+ uint64_t inro11 : 1;
+ uint64_t inro12 : 1;
+ uint64_t inro13 : 1;
+ uint64_t inro14 : 1;
+ uint64_t inro15 : 1;
+ uint64_t inro16 : 1;
+ uint64_t reqcogen : 1;
+ uint64_t reqco1 : 1;
+ uint64_t reqco2 : 1;
+ uint64_t reqco3 : 1;
+ uint64_t reqco4 : 1;
+ uint64_t unk_type_id : 1;
+ uint64_t unk_cause_tx : 1;
+ uint64_t unk_common_addr : 1;
+ uint64_t unk_info_addr : 1;
+};
+
+struct Iec104AsduCheck
+{
+ const Iec104ApciI* apci;
+ bool sq0Allowed;
+ bool sq1Allowed;
+ bool multipleIOAllowed;
+ Iec104AsduCheckCauseOfTx checkCauseOfTx;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_parse_information_object_elements.cc author Jared Rittle <jared.rittle@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "iec104_parse_information_object_elements.h"
+
+#include <math.h>
+
+#include "detection/detection_engine.h"
+#include "events/event_queue.h"
+#include "protocols/packet.h"
+
+#include "iec104.h"
+#include "iec104_decode.h"
+#include "iec104_module.h"
+
+using namespace snort;
+
+//
+// Information Object Structures Parsing
+//
+// This section contains functions to handle parsing and printing of the
+// various Information Object structures that make up the ASDU contents
+//
+
+// COI: Cause of Initialization Structure
+void parseIec104Coi(const Iec104CoiType* coi)
+{
+ // throw an alert when the cause is in the reserved ranges (3-127)
+ if (coi->ui >= IEC104_COI_UI_RES3)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_COI);
+ }
+}
+
+// QOI: Qualifier of Interrogation Structure
+void parseIec104Qoi(const Iec104QoiType* qoi)
+{
+ // throw an alert when the cause is in the reserved ranges
+ if (qoi->qoi >= IEC104_QOI_RES1 and qoi->qoi <= IEC104_QOI_RES19)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOI);
+ }
+ else if (qoi->qoi >= IEC104_QOI_RES37)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOI);
+ }
+}
+
+// QCC: Qualifier of Counter Interrogation Command Structure
+void parseIec104Qcc(const Iec104QccType* qcc)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (qcc->rqt >= IEC104_QCC_RQT_RES32)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QCC);
+ }
+}
+
+// QPM: Qualifier of Parameter of Measured Values Structure
+void parseIec104Qpm(const Iec104QpmType* qpm)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (qpm->kpa >= IEC104_QPM_KPA_RES5)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QPM_KPA);
+ }
+
+ if (qpm->lpc)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_ABNORMAL_QPM_LPC);
+ }
+
+ if (qpm->pop)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_ABNORMAL_QPM_POP);
+ }
+}
+
+// QPA: Qualifier of Parameter Activation Structure
+void parseIec104Qpa(const Iec104QpaType* qpa)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (qpa->qpa >= IEC104_QPA_RES4)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QPA);
+ }
+}
+
+// QOC: Qualifier of Command Structure
+void parseIec104Qoc(uint8_t qu, uint8_t se)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (qu >= IEC104_QOC_QU_RES4 and qu <= IEC104_QOC_QU_RES31)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOC);
+ }
+
+ if (se >= 2)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// QRP: Qualifier of Reset Process Structure
+void parseIec104Qrp(const Iec104QrpType* qrp)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (qrp->qrp >= IEC104_QRP_RES3)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QRP);
+ }
+}
+
+// FRQ: File Ready Qualifier Structure
+void parseIec104Frq(const Iec104FrqType* frq)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (frq->ui >= IEC104_FRQ_UI_RES1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_FRQ);
+ }
+}
+
+// SRQ: Section Ready Qualifier Structure
+void parseIec104Srq(const Iec104SrqType* srq)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (srq->ui >= IEC104_SRQ_UI_RES1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SRQ);
+ }
+}
+
+// SCQ: Select and Call Qualifier Structure
+void parseIec104Scq(const Iec104ScqType* scq)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (scq->ui1 >= IEC104_SCQ_UI1_RES8)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SCQ);
+ }
+
+ if (scq->ui2 >= IEC104_SCQ_UI2_RES6)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SCQ);
+ }
+}
+
+// LSQ: Last Section or Segment Qualifier Structure
+void parseIec104Lsq(const Iec104LsqType* lsq)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (lsq->lsq >= IEC104_LSQ_RES5)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_LSQ);
+ }
+}
+
+// AFQ: Acknowledge File or Section Qualifier Structure
+void parseIec104Afq(const Iec104AfqType* afq)
+{
+ // throw an alert when a reserved or invalid value is set
+ if (afq->ui1 >= IEC104_AFQ_UI1_RES5)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_AFQ);
+ }
+ if (afq->ui2 >= IEC104_AFQ_UI2_RES6)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_AFQ);
+ }
+}
+
+uint8_t parseIec104Vsq(const Iec104ApciI* apci)
+{
+ // number of elements == 0 is caught in check apdu
+
+ // make sure the reported number of elements would not exceed the packet size
+ // * take the apci->header.length value
+ // * subtract off type id, vsq, cause of tx, and 2-byte common address sizes
+ // * if the sq bit is set, subtract off the IOA
+ // * use a switch statement with cases of each message type to get the size of one group
+ // * divide the result of the earlier calculation by this group size to get the maximum allowable groups without overflowing
+
+ uint8_t maxNumberOfElements = 0;
+ uint32_t informationObjectSubgroupSize = 0;
+
+ // determine the size of the current message type group
+ switch(apci->asdu.typeId)
+ {
+ case IEC104_ASDU_M_SP_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_SP_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_SP_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_SP_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_DP_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_DP_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_DP_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_DP_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ST_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ST_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ST_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ST_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_BO_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_BO_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_BO_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_BO_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_NB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_NB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_NC_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_NC_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_TC_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_TC_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_IT_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_IT_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_IT_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_IT_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EP_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EP_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EP_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EP_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EP_TC_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EP_TC_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_PS_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_PS_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_ND_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_ND_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_SP_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_SP_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_DP_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_DP_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ST_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ST_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_BO_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_BO_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_TD_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_TD_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_TE_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_TE_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_ME_TF_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_ME_TF_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_IT_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_IT_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EP_TD_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EP_TD_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EP_TE_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EP_TE_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EP_TF_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EP_TF_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SC_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SC_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_DC_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_DC_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_RC_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_RC_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SE_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SE_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SE_NB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SE_NB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SE_NC_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SE_NC_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_BO_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_BO_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SC_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SC_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_DC_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_DC_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_RC_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_RC_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SE_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SE_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SE_TB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SE_TB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_SE_TC_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_SE_TC_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_BO_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_BO_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_M_EI_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104M_EI_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_IC_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_IC_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_CI_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_CI_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_RD_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_RD_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_CS_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_CS_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_TS_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_TS_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_RP_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_RP_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_CD_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_CD_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_C_TS_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104C_TS_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_P_ME_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104P_ME_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_P_ME_NB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104P_ME_NB_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_P_ME_NC_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104P_ME_NC_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_P_AC_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104P_AC_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_FR_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_FR_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_SR_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_SR_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_SC_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_SC_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_LS_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_LS_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_AF_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_AF_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_SG_NA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_SG_NA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_DR_TA_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_DR_TA_1_IO_Subgroup);
+ break;
+ }
+ case IEC104_ASDU_F_SC_NB_1:
+ {
+ informationObjectSubgroupSize = sizeof(Iec104F_SC_NB_1_IO_Subgroup);
+ break;
+ }
+ }
+
+ if (informationObjectSubgroupSize)
+ {
+ if (apci->asdu.variableStructureQualifier.sq == 0)
+ {
+ uint32_t informationObjectGroupSize = informationObjectSubgroupSize + sizeof(const Iec104InformationObjectAddressThreeOctetType);
+ maxNumberOfElements = (apci->header.length
+ - sizeof(uint8_t) // type id
+ - sizeof(const Iec104VariableStructureQualifierType)
+ - sizeof(const Iec104CauseOfTransmissionType)
+ - sizeof(const Iec104CommonAddressOfAsduType)
+ ) / informationObjectGroupSize;
+ }
+ else
+ {
+ maxNumberOfElements = (apci->header.length
+ - sizeof(uint8_t) // type id
+ - sizeof(const Iec104VariableStructureQualifierType)
+ - sizeof(const Iec104CauseOfTransmissionType)
+ - sizeof(const Iec104CommonAddressOfAsduType)
+ - sizeof(const Iec104InformationObjectAddressThreeOctetType)
+ ) / informationObjectSubgroupSize;
+ }
+ }
+
+ uint8_t verifiedNumberOfElements = apci->asdu.variableStructureQualifier.numberOfElements;
+ if (verifiedNumberOfElements > 0 and verifiedNumberOfElements <= maxNumberOfElements)
+ {
+ // do nothing
+ }
+ else
+ {
+ verifiedNumberOfElements = 0;
+ DetectionEngine::queue_event(GID_IEC104, IEC104_APCII_INVALID_NUM_ELEMENTS_VALUE);
+ }
+
+ // if the SQ is set and the number of elements is only one something is off
+ // this case does not apply in cases where the SQ bit being set is the only option
+ // the only place this is known to exist is in F_DR_TA_1
+ if (apci->asdu.variableStructureQualifier.sq > 0
+ and apci->asdu.variableStructureQualifier.numberOfElements == 1
+ and apci->asdu.typeId != IEC104_ASDU_F_DR_TA_1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_VSQ_ABNORMAL_SQ);
+ }
+
+
+ return verifiedNumberOfElements;
+}
+
+void parseIec104CauseOfTx(const Iec104ApciI* apci)
+{
+ // no alerts are needed here as they are processed in checkIec104Asdu
+
+ if (!apci)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+void parseIec104TwoOctetCommonAddress(const Iec104ApciI* apci)
+{
+ // provide an alert if a null common address is provided
+ if (apci->asdu.commonAddressOfAsdu.commonAddress == 0)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_COMMON_ADDRESS);
+ }
+}
+
+void parseIec104InformationObjectAddressWithThreeOctets(
+ const Iec104InformationObjectAddressThreeOctetType* ioa)
+{
+ // Nothing worth alerting on here
+
+ if (!ioa)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// SIQ: Single Point Information with Quality Descriptor Structure
+void parseIec104Siq(const Iec104SiqType* siq)
+{
+ // provide an alert if the reserved field is used
+ if (siq->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SIQ);
+ }
+}
+
+// DIQ: Double Point Information with Quality Descriptor Structure
+void parseIec104Diq(const Iec104DiqType* diq)
+{
+ // provide an alert if the reserved field is used
+ if (diq->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_DIQ);
+ }
+}
+
+// QDS: Quality Descriptor Structure
+void parseIec104Qds(const Iec104QdsType* qds)
+{
+ // provide an alert if the reserved field is used
+ if (qds->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QDS);
+ }
+}
+
+// QDP: Quality Descriptor for Events of Protection Equipment Structure
+void parseIec104Qdp(const Iec104QdpType* qdp)
+{
+ // provide an alert if the reserved field is used
+ if (qdp->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QDP);
+ }
+}
+
+// VTI: Value with Transient State Indication Structure
+void parseIec104Vti(const Iec104VtiType* vti)
+{
+ // Nothing worth alerting on here
+
+ if (!vti)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// NVA: Normalized Value Structure
+void parseIec104Nva(const Iec104NvaType* nva)
+{
+ // Nothing worth alerting on here
+
+ if (!nva)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// SVA: Scaled Value Structure
+void parseIec104Sva(const Iec104SvaType* sva)
+{
+ // Nothing worth alerting on here
+
+ if (!sva)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// IEEE_STD_754: Short Floating Point Number Structure
+void parseIec104IeeeStd754(const Iec104IeeeStd754Type* ieeeStd754)
+{
+ //FIXIT-E: keep investigating possible alerts here
+
+ // convert the passed IEEE Std 754 value to big endian
+ uint32_t fixedIeeeStd754 = htonl(ieeeStd754->ieeeStd754);
+
+ // break out individual fields for calculation
+ // f == fraction, e == exponent, s == sign
+ // +-----------------------------------------------------------------+
+ // | 1 0 |
+ // | f e d c b a 9 8 7 6 5 4 3 2 1 0 f e d c b a 9 8 7 6 5 4 3 2 1 0 |
+ // +-----------------------------------------------------------------+
+ // | s e e e e e e e e f f f f f f f f f f f f f f f f f f f f f f f |
+ // +-----------------------------------------------------------------+
+ uint32_t ieeeStd754RawFraction = fixedIeeeStd754 & 0x007FFFFF;
+ uint8_t ieeeStd754RawExponent = (fixedIeeeStd754 >> 0x17) & 0xFF;
+
+ // true exponent cannot be above 127 (raw 0xff)
+ if (ieeeStd754RawExponent == 0xFF)
+ {
+ // alert on infinity if raw exponent == 0xff and fraction == 0x00
+ if (ieeeStd754RawFraction == 0)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_IEEE_STD_754_INFINITY);
+ }
+ // alert on NaN if raw exponent == 0xff and fraction > 0x00
+ else
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_IEEE_STD_754_NAN);
+ }
+ }
+}
+
+// BCR: Binary Counter Reading Structure
+void parseIec104Bcr(const Iec104BcrType* bcr)
+{
+ // Nothing worth alerting on here
+
+ if (!bcr)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// SEP: Single Event of Protection Equipment Structure
+void parseIec104Sep(const Iec104SepType* sep)
+{
+ // provide an alert if the reserved field is used
+ if (sep->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SEP);
+ }
+}
+
+// SPE: Start Event of Protection Equipment Structure
+void parseIec104Spe(const Iec104SpeType* spe)
+{
+ // provide an alert if the reserved field is used
+ if (spe->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SPE);
+ }
+}
+
+// OCI: Output Circuit Information Structure
+void parseIec104Oci(const Iec104OciType* oci)
+{
+ // provide an alert if the reserved field is used
+ if (oci->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_OCI);
+ }
+}
+
+// BSI: Binary State Information Structure
+void parseIec104Bsi(const Iec104BsiType* bsi)
+{
+ // Nothing worth alerting on here
+
+ if (!bsi)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// FBP: Fixed Test Bit Pattern Structure
+void parseIec104Fbp(const Iec104FbpType* fbp)
+{
+ // provide an alert if the FBP is not \x55\xAA
+ if (fbp->fixedTestBitPattern != 0x55AA)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_FBP);
+ }
+}
+
+// SCO: Single Command Structure
+void parseIec104Sco(const Iec104ScoType* sco)
+{
+ // provide an alert if the reserved field is used
+ if (sco->reserved)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SCO);
+ }
+
+ // parse the Qualifier of Command structure
+ parseIec104Qoc(sco->qu, sco->se);
+}
+
+// DCO: Double Command Structure
+void parseIec104Dco(const Iec104DcoType* dco)
+{
+ // throw an alert when one of the defined invalid command states are detected
+ if (dco->dcs == IEC104_DCO_DCS_NOTPERMITTED1 or dco->dcs == IEC104_DCO_DCS_NOTPERMITTED2)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DCO);
+ }
+
+ // parse the Qualifier of Command structure
+ parseIec104Qoc(dco->qu, dco->se);
+}
+
+// RCO: Regulating Step Command Structure
+void parseIec104Rco(const Iec104RcoType* rco)
+{
+ // throw an alert when one of the defined invalid command states are detected
+ if (rco->rcs == IEC104_RCO_RCS_NOTPERMITTED1 or rco->rcs == IEC104_RCO_RCS_NOTPERMITTED2)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_RCO);
+ }
+
+ // parse the Qualifier of Command structure
+ parseIec104Qoc(rco->qu, rco->se);
+}
+
+// Time2a Milliseconds Structure
+void parseIec104Time2aMilliseconds(const Iec104Time2aMillisecondsType* time2aMilliseconds)
+{
+ // ensure milliseconds aren't over the maximum allowed value
+ if (time2aMilliseconds->milliseconds >= IEC104_MS_IN_MINUTE)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_MS_IN_MINUTE);
+ }
+}
+
+// Time2a IVResMinute Structure
+void parseIec104Time2aIvresminute(const Iec104Time2aIvresminuteType* time2aIvresminute)
+{
+ // ensure minutes arent over 59
+ if (time2aIvresminute->minutes >= IEC104_MINS_IN_HOUR)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_MINS_IN_HOUR);
+ }
+
+ // provide an alert if the reserved field is used
+ if (time2aIvresminute->res)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_MINS_IN_HOUR);
+ }
+}
+
+// Time2a SURes2Hour Structure
+void parseIec104Time2aSures2hour(const Iec104Time2aSures2hourType* time2aSures2hour)
+{
+ // ensure hours arent over 23
+ if (time2aSures2hour->hours >= IEC104_HOURS_IN_DAY)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_HOURS_IN_DAY);
+ }
+
+ // provide an alert if the reserved field is used
+ if (time2aSures2hour->res2)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_HOURS_IN_DAY);
+ }
+}
+
+static bool isLeapYear(uint32_t yearOffset)
+{
+ // need to make sure we use the real year value and not just the offset
+ uint32_t trueYear = IEC104_TIME2ARES4YEAR_BASE + yearOffset;
+
+ // determine if the current year matches the following criteria:
+ // (ref: https://docs.microsoft.com/en-us/office/troubleshoot/excel/determine-a-leap-year)
+ // 1. If the year is evenly divisible by 4, go to step 2. Otherwise, go to step 5.
+ // 2. If the year is evenly divisible by 100, go to step 3. Otherwise, go to step 4.
+ // 3. If the year is evenly divisible by 400, go to step 4. Otherwise, go to step 5.
+ // 4. The year is a leap year (it has 366 days).
+ // 5. The year is not a leap year (it has 365 days).
+
+ if (trueYear % 4 == 0)
+ {
+ if (trueYear % 100 == 0)
+ {
+ if (trueYear % 400 == 0)
+ {
+ // year is evenly divisible by 4, 100, and 400
+ // leap year
+ return true;
+ }
+ else
+ {
+ // year is evenly divisible by 4, and 100 but NOT 400
+ // NOT a leap year
+ return false;
+ }
+ }
+ else
+ {
+ // year is evenly divisible by 4 but not evenly divisible by 100
+ // leap year
+ return true;
+ }
+ }
+ else
+ {
+ // year is not evenly divisible by 4
+ // NOT a leap year
+ return false;
+ }
+}
+
+// Time2a DOWDay Structure
+void parseIec104Time2aDowday(const Iec104Cp56Time2aType* sevenOctetBinaryTime)
+{
+ // Day of week will always be between 0 and 7 since the field is only 3 bits
+ // make sure month is at least 1 and no more than 12
+ if (sevenOctetBinaryTime->res3month.month >= IEC104_MONTH_JAN
+ and sevenOctetBinaryTime->res3month.month <= IEC104_MONTH_DEC)
+ {
+ // do in depth datetime analysis
+ if (sevenOctetBinaryTime->res3month.month == IEC104_MONTH_FEB)
+ {
+ // handle leap year first
+ if (isLeapYear(sevenOctetBinaryTime->res4year.year))
+ {
+ if (sevenOctetBinaryTime->dowday.dayOfMonth > IEC104_MAX_DAYOFMONTH_FEB_LEAPYEAR)
+ {
+ // "CP56Time2a Day of Month set outside of the allowable range for leap year
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DAY_OF_MONTH);
+ }
+ }
+ else
+ {
+ if (sevenOctetBinaryTime->dowday.dayOfMonth > IEC104_MAX_DAYOFMONTH_FEB_NONLEAPYEAR)
+ {
+ // CP56Time2a Day of Month set outside of the allowable range for non-leap year
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DAY_OF_MONTH);
+ }
+ }
+ // handle all months with 30 days
+ }
+ else if (sevenOctetBinaryTime->res3month.month == IEC104_MONTH_APR
+ or sevenOctetBinaryTime->res3month.month == IEC104_MONTH_JUN
+ or sevenOctetBinaryTime->res3month.month == IEC104_MONTH_SEP
+ or sevenOctetBinaryTime->res3month.month == IEC104_MONTH_NOV)
+ {
+ if (sevenOctetBinaryTime->dowday.dayOfMonth > IEC104_MAX_DAYOFMONTH_30)
+ {
+ // CP56Time2a Day of Month set outside of the allowable range for 30-day months
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_DAY_OF_MONTH);
+ }
+
+ }// months with 31 days cannot be over as the type isn't large enough
+ }
+ else
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// Time2a Res3Month Structure
+void parseIec104Time2aRes3month(const Iec104Time2aRes3monthType* time2aRes3month)
+{
+ // ensure month is not over 12 (December)
+ if (time2aRes3month->month < IEC104_MONTH_JAN or time2aRes3month->month > IEC104_MONTH_DEC)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_MONTH);
+ }
+
+ // provide an alert if the reserved field is used
+ if (time2aRes3month->res3)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_MONTH);
+ }
+}
+
+// Time2a Res4Year Structure
+void parseIec104Time2aRes4year(const Iec104Time2aRes4yearType* time2aRes4year)
+{
+ // ensure the year isn't before 1970 or after 2027
+ // the year field is treated as an offset from the year 1900
+ // so 1970 == 70 and 2027 == 127
+ // 2027 was chosen as an end date as the time2aRes4year->year field is only 7 bits
+ if ((int) time2aRes4year->year < IEC104_TIME2ARES4YEAR_1970)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_YEAR);
+ }
+
+ // provide an alert if the reserved field is used
+ if (time2aRes4year->res4)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_YEAR);
+ }
+}
+
+// CP56Time2a Structure
+void parseIec104Cp56Time2a(const Iec104Cp56Time2aType* sevenOctetBinaryTime)
+{
+ // Nothing worth alerting on directly here
+
+ parseIec104Time2aMilliseconds(&sevenOctetBinaryTime->milliseconds);
+ parseIec104Time2aIvresminute(&sevenOctetBinaryTime->ivresminute);
+ parseIec104Time2aSures2hour(&sevenOctetBinaryTime->sures2hour);
+ parseIec104Time2aDowday(sevenOctetBinaryTime); // need to pass the entire time struct for full error checking
+ parseIec104Time2aRes3month(&sevenOctetBinaryTime->res3month);
+ parseIec104Time2aRes4year(&sevenOctetBinaryTime->res4year);
+}
+
+// Cp24Time2a Structure
+void parseIec104Cp24Time2a(const Iec104Cp24Time2aType* threeOctetBinaryTime)
+{
+ // Nothing worth alerting on directly here
+
+ parseIec104Time2aMilliseconds(&threeOctetBinaryTime->milliseconds);
+ parseIec104Time2aIvresminute(&threeOctetBinaryTime->ivresminute);
+}
+
+// Cp16Time2a Structure
+void parseIec104Cp16Time2a(const Iec104Cp16Time2aType* cp16Time2a)
+{
+ // Nothing worth alerting on directly here
+
+ parseIec104Time2aMilliseconds(&cp16Time2a->milliseconds);
+}
+
+// NOF: Name of File Structure
+void parseIec104Nof(const Iec104NofType* nof)
+{
+ // Nothing worth alerting on directly here
+
+ if (!nof)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// NOS: Name of Section Structure
+void parseIec104Nos(const Iec104NosType* nos)
+{
+ // Nothing worth alerting on directly here
+
+ if (!nos)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// LOF: Length of File or Section Structure
+void parseIec104Lof(const Iec104LofType* lof)
+{
+ // maybe a rule checking if length of file is greater than amount of data
+ // It appears that the length field here is an indicator for other messages actually containing the
+ // file data so detection may be better via plaintext rules with flowbits if desired
+
+ if (!lof)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// LOS: Length of Segment Structure
+bool parseIec104Los(const Iec104LosType* los, uint16_t apduSize)
+{
+ // flag to prevent debug parsing of the segments when an alert is thrown
+ // doing this via a flag so that the debug messages for the LOS field still print
+ bool losValid = true;
+
+ // number of bytes counted in the length field before the LOS field
+ uint16_t losPrecedingBytes = 0x11;
+
+ // a segment length of zero is not expected
+ if (los->lengthOfSegment == 0)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_NULL_LOS_VALUE);
+ losValid = false;
+ }
+ // since the los value indicates the number of octets in the segment and it is only used
+ // in ASDU types that cannot have multiple number of items, the los value times 8 should
+ // always equal the remaining number of bytes in the message
+ // we can calculate this number by taking the apduSize (which has been checked for tampering
+ // earlier) and subtracting the number of bytes preceding the los field (0x11)
+ else if (los->lengthOfSegment != (apduSize - losPrecedingBytes))
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_INVALID_LOS_VALUE);
+ losValid = false;
+ }
+
+ return losValid;
+}
+
+// CHS: Checksum Structure
+void parseIec104Chs(const Iec104ChsType* chs)
+{
+ // Nothing worth alerting on directly here
+
+ if (!chs)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// SOF: Status of File Structure
+void parseIec104Sof(const Iec104SofType* sof)
+{
+ // provide an alert if the reserved field is used
+ if (sof->sofStatus >= IEC104_SOF_STATUS_RES1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_SOF);
+ }
+}
+
+// QOS: Qualifier of Set Point Command Structure
+void parseIec104Qos(const Iec104QosType* qos)
+{
+ // provide an alert if the reserved field is used
+ if (qos->ql >= IEC104_QOS_QL_RES1)
+ {
+ DetectionEngine::queue_event(GID_IEC104, IEC104_RESERVED_QOS);
+ }
+}
+
+// SCD: Status + Status Change Detection Structure
+void parseIec104Scd(const Iec104ScdType* scd)
+{
+ // Nothing worth alerting on directly here
+
+ if (!scd)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// TSC: Test Sequence Counter
+void parseIec104Tsc(const Iec104TscType* tsc)
+{
+ // Nothing worth alerting on directly here
+
+ if (!tsc)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
+// Segment: Segment type
+void parseIec104Segment(const Iec104SegmentType* segment)
+{
+ // Nothing worth alerting on directly here
+
+ if (!segment)
+ {
+ // error indicating that parsing couldn't finish
+ }
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_parse_information_object_elements.h author Jared Rittle <jared.rittle@cisco.com>
+
+#ifndef IEC104_PARSE_INFORMATION_OBJECT_ELEMENTS_H
+#define IEC104_PARSE_INFORMATION_OBJECT_ELEMENTS_H
+
+#include "iec104_parse_apdu.h"
+
+#define IEC104_MS_IN_MINUTE 60000
+#define IEC104_MINS_IN_HOUR 60
+#define IEC104_HOURS_IN_DAY 24
+#define IEC104_TIME2ARES4YEAR_BASE 1900
+#define IEC104_TIME2ARES4YEAR_1970 70
+#define IEC104_TIME2ARES4YEAR_2027 127
+#define IEC104_MAX_DAYOFMONTH_FEB_LEAPYEAR 29
+#define IEC104_MAX_DAYOFMONTH_FEB_NONLEAPYEAR 28
+#define IEC104_MAX_DAYOFMONTH_30 30
+#define IEC104_MAX_DAYOFMONTH_31 31
+
+void parseIec104Coi(const Iec104CoiType* coi);
+void parseIec104Qoi(const Iec104QoiType* qoi);
+void parseIec104Qcc(const Iec104QccType* qcc);
+void parseIec104Qpm(const Iec104QpmType* qpm);
+void parseIec104Qpa(const Iec104QpaType* qpa);
+void parseIec104Qoc(uint8_t qu, uint8_t se);
+void parseIec104Qrp(const Iec104QrpType* qrp);
+void parseIec104Frq(const Iec104FrqType* frq);
+void parseIec104Srq(const Iec104SrqType* srq);
+void parseIec104Scq(const Iec104ScqType* scq);
+void parseIec104Lsq(const Iec104LsqType* lsq);
+void parseIec104Afq(const Iec104AfqType* afq);
+uint8_t parseIec104Vsq(const Iec104ApciI* apci);
+void parseIec104CauseOfTx(const Iec104ApciI* apci);
+void parseIec104TwoOctetCommonAddress(const Iec104ApciI* apci);
+void parseIec104InformationObjectAddressWithThreeOctets(
+ const Iec104InformationObjectAddressThreeOctetType* ioa);
+void parseIec104Siq(const Iec104SiqType* siq);
+void parseIec104Diq(const Iec104DiqType* diq);
+void parseIec104Qds(const Iec104QdsType* qds);
+void parseIec104Qdp(const Iec104QdpType* qdp);
+void parseIec104Vti(const Iec104VtiType* vti);
+void parseIec104Nva(const Iec104NvaType* nva);
+void parseIec104Sva(const Iec104SvaType* sva);
+void parseIec104IeeeStd754(const Iec104IeeeStd754Type* ieeeStd754);
+void parseIec104Bcr(const Iec104BcrType* bcr);
+void parseIec104Sep(const Iec104SepType* sep);
+void parseIec104Spe(const Iec104SpeType* spe);
+void parseIec104Oci(const Iec104OciType* oci);
+void parseIec104Bsi(const Iec104BsiType* bsi);
+void parseIec104Fbp(const Iec104FbpType* fbp);
+void parseIec104Sco(const Iec104ScoType* sco);
+void parseIec104Dco(const Iec104DcoType* dco);
+void parseIec104Rco(const Iec104RcoType* rco);
+void parseIec104Time2aMilliseconds(const Iec104Time2aMillisecondsType* time2aMilliseconds);
+void parseIec104Time2aIvresminute(const Iec104Time2aIvresminuteType* time2aIvresminute);
+void parseIec104Time2aSures2hour(const Iec104Time2aSures2hourType* time2aSures2hour);
+void parseIec104Time2aDowday(const Iec104Cp56Time2aType* sevenOctetBinaryTime);
+void parseIec104Time2aRes3month(const Iec104Time2aRes3monthType* time2aRes3month);
+void parseIec104Time2aRes4year(const Iec104Time2aRes4yearType* time2aRes4year);
+void parseIec104Cp56Time2a(const Iec104Cp56Time2aType* sevenOctetBinaryTime);
+void parseIec104Cp24Time2a(const Iec104Cp24Time2aType* threeOctetBinaryTime);
+void parseIec104Cp16Time2a(const Iec104Cp16Time2aType* cp16Time2a);
+void parseIec104Nof(const Iec104NofType* nof);
+void parseIec104Nos(const Iec104NosType* nos);
+void parseIec104Lof(const Iec104LofType* lof);
+bool parseIec104Los(const Iec104LosType* los, uint16_t apduSize);
+void parseIec104Chs(const Iec104ChsType* chs);
+void parseIec104Sof(const Iec104SofType* sof);
+void parseIec104Qos(const Iec104QosType* qos);
+void parseIec104Scd(const Iec104ScdType* scd);
+void parseIec104Tsc(const Iec104TscType* tsc);
+void parseIec104Segment(const Iec104SegmentType* segment);
+
+struct GenericIec104AsduIOGroup
+{
+ bool includeIOA;
+ uint16_t apduSize;
+ uint32_t asduType;
+ union
+ {
+ const Iec104M_SP_NA_1_IO_Group* m_sp_na_1IOGroup;
+ const Iec104M_SP_TA_1_IO_Group* m_sp_ta_1IOGroup;
+ const Iec104M_DP_NA_1_IO_Group* m_dp_na_1IOGroup;
+ const Iec104M_DP_TA_1_IO_Group* m_dp_ta_1IOGroup;
+ const Iec104M_ST_NA_1_IO_Group* m_st_na_1IOGroup;
+ const Iec104M_ST_TA_1_IO_Group* m_st_ta_1IOGroup;
+ const Iec104M_BO_NA_1_IO_Group* m_bo_na_1IOGroup;
+ const Iec104M_BO_TA_1_IO_Group* m_bo_ta_1IOGroup;
+ const Iec104M_ME_NA_1_IO_Group* m_me_na_1IOGroup;
+ const Iec104M_ME_TA_1_IO_Group* m_me_ta_1IOGroup;
+ const Iec104M_ME_NB_1_IO_Group* m_me_nb_1IOGroup;
+ const Iec104M_ME_TB_1_IO_Group* m_me_tb_1IOGroup;
+ const Iec104M_ME_NC_1_IO_Group* m_me_nc_1IOGroup;
+ const Iec104M_ME_TC_1_IO_Group* m_me_tc_1IOGroup;
+ const Iec104M_IT_NA_1_IO_Group* m_it_na_1IOGroup;
+ const Iec104M_IT_TA_1_IO_Group* m_it_ta_1IOGroup;
+ const Iec104M_EP_TA_1_IO_Group* m_ep_ta_1IOGroup;
+ const Iec104M_EP_TB_1_IO_Group* m_ep_tb_1IOGroup;
+ const Iec104M_EP_TC_1_IO_Group* m_ep_tc_1IOGroup;
+ const Iec104M_PS_NA_1_IO_Group* m_ps_na_1IOGroup;
+ const Iec104M_ME_ND_1_IO_Group* m_me_nd_1IOGroup;
+ const Iec104M_SP_TB_1_IO_Group* m_sp_tb_1IOGroup;
+ const Iec104M_DP_TB_1_IO_Group* m_dp_tb_1IOGroup;
+ const Iec104M_ST_TB_1_IO_Group* m_st_tb_1IOGroup;
+ const Iec104M_BO_TB_1_IO_Group* m_bo_tb_1IOGroup;
+ const Iec104M_ME_TD_1_IO_Group* m_me_td_1IOGroup;
+ const Iec104M_ME_TE_1_IO_Group* m_me_te_1IOGroup;
+ const Iec104M_ME_TF_1_IO_Group* m_me_tf_1IOGroup;
+ const Iec104M_IT_TB_1_IO_Group* m_it_tb_1IOGroup;
+ const Iec104M_EP_TD_1_IO_Group* m_ep_td_1IOGroup;
+ const Iec104M_EP_TE_1_IO_Group* m_ep_te_1IOGroup;
+ const Iec104M_EP_TF_1_IO_Group* m_ep_tf_1IOGroup;
+ const Iec104C_SC_NA_1_IO_Group* c_sc_na_1IOGroup;
+ const Iec104C_DC_NA_1_IO_Group* c_dc_na_1IOGroup;
+ const Iec104C_RC_NA_1_IO_Group* c_rc_na_1IOGroup;
+ const Iec104C_SE_NA_1_IO_Group* c_se_na_1IOGroup;
+ const Iec104C_SE_NB_1_IO_Group* c_se_nb_1IOGroup;
+ const Iec104C_SE_NC_1_IO_Group* c_se_nc_1IOGroup;
+ const Iec104C_BO_NA_1_IO_Group* c_bo_na_1IOGroup;
+ const Iec104C_SC_TA_1_IO_Group* c_sc_ta_1IOGroup;
+ const Iec104C_DC_TA_1_IO_Group* c_dc_ta_1IOGroup;
+ const Iec104C_RC_TA_1_IO_Group* c_rc_ta_1IOGroup;
+ const Iec104C_SE_TA_1_IO_Group* c_se_ta_1IOGroup;
+ const Iec104C_SE_TB_1_IO_Group* c_se_tb_1IOGroup;
+ const Iec104C_SE_TC_1_IO_Group* c_se_tc_1IOGroup;
+ const Iec104C_BO_TA_1_IO_Group* c_bo_ta_1IOGroup;
+ const Iec104M_EI_NA_1_IO_Group* m_ei_na_1IOGroup;
+ const Iec104C_IC_NA_1_IO_Group* c_ic_na_1IOGroup;
+ const Iec104C_CI_NA_1_IO_Group* c_ci_na_1IOGroup;
+ const Iec104C_RD_NA_1_IO_Group* c_rd_na_1IOGroup;
+ const Iec104C_CS_NA_1_IO_Group* c_cs_na_1IOGroup;
+ const Iec104C_TS_NA_1_IO_Group* c_ts_na_1IOGroup;
+ const Iec104C_RP_NA_1_IO_Group* c_rp_na_1IOGroup;
+ const Iec104C_CD_NA_1_IO_Group* c_cd_na_1IOGroup;
+ const Iec104C_TS_TA_1_IO_Group* c_ts_ta_1IOGroup;
+ const Iec104P_ME_NA_1_IO_Group* p_me_na_1IOGroup;
+ const Iec104P_ME_NB_1_IO_Group* p_me_nb_1IOGroup;
+ const Iec104P_ME_NC_1_IO_Group* p_me_nc_1IOGroup;
+ const Iec104P_AC_NA_1_IO_Group* p_ac_na_1IOGroup;
+ const Iec104F_FR_NA_1_IO_Group* f_fr_na_1IOGroup;
+ const Iec104F_SR_NA_1_IO_Group* f_sr_na_1IOGroup;
+ const Iec104F_SC_NA_1_IO_Group* f_sc_na_1IOGroup;
+ const Iec104F_LS_NA_1_IO_Group* f_ls_na_1IOGroup;
+ const Iec104F_AF_NA_1_IO_Group* f_af_na_1IOGroup;
+ const Iec104F_SG_NA_1_IO_Group* f_sg_na_1IOGroup;
+ const Iec104F_DR_TA_1_IO_Group* f_dr_ta_1IOGroup;
+ const Iec104F_SC_NB_1_IO_Group* f_sc_nb_1IOGroup;
+ };
+ union
+ {
+ const Iec104M_SP_NA_1_IO_Subgroup* m_sp_na_1IOSubgroup;
+ const Iec104M_SP_TA_1_IO_Subgroup* m_sp_ta_1IOSubgroup;
+ const Iec104M_DP_NA_1_IO_Subgroup* m_dp_na_1IOSubgroup;
+ const Iec104M_DP_TA_1_IO_Subgroup* m_dp_ta_1IOSubgroup;
+ const Iec104M_ST_NA_1_IO_Subgroup* m_st_na_1IOSubgroup;
+ const Iec104M_ST_TA_1_IO_Subgroup* m_st_ta_1IOSubgroup;
+ const Iec104M_BO_NA_1_IO_Subgroup* m_bo_na_1IOSubgroup;
+ const Iec104M_BO_TA_1_IO_Subgroup* m_bo_ta_1IOSubgroup;
+ const Iec104M_ME_NA_1_IO_Subgroup* m_me_na_1IOSubgroup;
+ const Iec104M_ME_TA_1_IO_Subgroup* m_me_ta_1IOSubgroup;
+ const Iec104M_ME_NB_1_IO_Subgroup* m_me_nb_1IOSubgroup;
+ const Iec104M_ME_TB_1_IO_Subgroup* m_me_tb_1IOSubgroup;
+ const Iec104M_ME_NC_1_IO_Subgroup* m_me_nc_1IOSubgroup;
+ const Iec104M_ME_TC_1_IO_Subgroup* m_me_tc_1IOSubgroup;
+ const Iec104M_IT_NA_1_IO_Subgroup* m_it_na_1IOSubgroup;
+ const Iec104M_IT_TA_1_IO_Subgroup* m_it_ta_1IOSubgroup;
+ const Iec104M_EP_TA_1_IO_Subgroup* m_ep_ta_1IOSubgroup;
+ const Iec104M_EP_TB_1_IO_Subgroup* m_ep_tb_1IOSubgroup;
+ const Iec104M_EP_TC_1_IO_Subgroup* m_ep_tc_1IOSubgroup;
+ const Iec104M_PS_NA_1_IO_Subgroup* m_ps_na_1IOSubgroup;
+ const Iec104M_ME_ND_1_IO_Subgroup* m_me_nd_1IOSubgroup;
+ const Iec104M_SP_TB_1_IO_Subgroup* m_sp_tb_1IOSubgroup;
+ const Iec104M_DP_TB_1_IO_Subgroup* m_dp_tb_1IOSubgroup;
+ const Iec104M_ST_TB_1_IO_Subgroup* m_st_tb_1IOSubgroup;
+ const Iec104M_BO_TB_1_IO_Subgroup* m_bo_tb_1IOSubgroup;
+ const Iec104M_ME_TD_1_IO_Subgroup* m_me_td_1IOSubgroup;
+ const Iec104M_ME_TE_1_IO_Subgroup* m_me_te_1IOSubgroup;
+ const Iec104M_ME_TF_1_IO_Subgroup* m_me_tf_1IOSubgroup;
+ const Iec104M_IT_TB_1_IO_Subgroup* m_it_tb_1IOSubgroup;
+ const Iec104M_EP_TD_1_IO_Subgroup* m_ep_td_1IOSubgroup;
+ const Iec104M_EP_TE_1_IO_Subgroup* m_ep_te_1IOSubgroup;
+ const Iec104M_EP_TF_1_IO_Subgroup* m_ep_tf_1IOSubgroup;
+ const Iec104C_SC_NA_1_IO_Subgroup* c_sc_na_1IOSubgroup;
+ const Iec104C_DC_NA_1_IO_Subgroup* c_dc_na_1IOSubgroup;
+ const Iec104C_RC_NA_1_IO_Subgroup* c_rc_na_1IOSubgroup;
+ const Iec104C_SE_NA_1_IO_Subgroup* c_se_na_1IOSubgroup;
+ const Iec104C_SE_NB_1_IO_Subgroup* c_se_nb_1IOSubgroup;
+ const Iec104C_SE_NC_1_IO_Subgroup* c_se_nc_1IOSubgroup;
+ const Iec104C_BO_NA_1_IO_Subgroup* c_bo_na_1IOSubgroup;
+ const Iec104C_SC_TA_1_IO_Subgroup* c_sc_ta_1IOSubgroup;
+ const Iec104C_DC_TA_1_IO_Subgroup* c_dc_ta_1IOSubgroup;
+ const Iec104C_RC_TA_1_IO_Subgroup* c_rc_ta_1IOSubgroup;
+ const Iec104C_SE_TA_1_IO_Subgroup* c_se_ta_1IOSubgroup;
+ const Iec104C_SE_TB_1_IO_Subgroup* c_se_tb_1IOSubgroup;
+ const Iec104C_SE_TC_1_IO_Subgroup* c_se_tc_1IOSubgroup;
+ const Iec104C_BO_TA_1_IO_Subgroup* c_bo_ta_1IOSubgroup;
+ const Iec104M_EI_NA_1_IO_Subgroup* m_ei_na_1IOSubgroup;
+ const Iec104C_IC_NA_1_IO_Subgroup* c_ic_na_1IOSubgroup;
+ const Iec104C_CI_NA_1_IO_Subgroup* c_ci_na_1IOSubgroup;
+ const Iec104C_RD_NA_1_IO_Subgroup* c_rd_na_1IOSubgroup;
+ const Iec104C_CS_NA_1_IO_Subgroup* c_cs_na_1IOSubgroup;
+ const Iec104C_TS_NA_1_IO_Subgroup* c_ts_na_1IOSubgroup;
+ const Iec104C_RP_NA_1_IO_Subgroup* c_rp_na_1IOSubgroup;
+ const Iec104C_CD_NA_1_IO_Subgroup* c_cd_na_1IOSubgroup;
+ const Iec104C_TS_TA_1_IO_Subgroup* c_ts_ta_1IOSubgroup;
+ const Iec104P_ME_NA_1_IO_Subgroup* p_me_na_1IOSubgroup;
+ const Iec104P_ME_NB_1_IO_Subgroup* p_me_nb_1IOSubgroup;
+ const Iec104P_ME_NC_1_IO_Subgroup* p_me_nc_1IOSubgroup;
+ const Iec104P_AC_NA_1_IO_Subgroup* p_ac_na_1IOSubgroup;
+ const Iec104F_FR_NA_1_IO_Subgroup* f_fr_na_1IOSubgroup;
+ const Iec104F_SR_NA_1_IO_Subgroup* f_sr_na_1IOSubgroup;
+ const Iec104F_SC_NA_1_IO_Subgroup* f_sc_na_1IOSubgroup;
+ const Iec104F_LS_NA_1_IO_Subgroup* f_ls_na_1IOSubgroup;
+ const Iec104F_AF_NA_1_IO_Subgroup* f_af_na_1IOSubgroup;
+ const Iec104F_SG_NA_1_IO_Subgroup* f_sg_na_1IOSubgroup;
+ const Iec104F_DR_TA_1_IO_Subgroup* f_dr_ta_1IOSubgroup;
+ const Iec104F_SC_NB_1_IO_Subgroup* f_sc_nb_1IOSubgroup;
+ };
+} __attribute__((packed));
+
+enum Iec104MonthEnum
+{
+ IEC104_MONTH_JAN = 1,
+ IEC104_MONTH_FEB = 2,
+ IEC104_MONTH_MAR = 3,
+ IEC104_MONTH_APR = 4,
+ IEC104_MONTH_MAY = 5,
+ IEC104_MONTH_JUN = 6,
+ IEC104_MONTH_JUL = 7,
+ IEC104_MONTH_AUG = 8,
+ IEC104_MONTH_SEP = 9,
+ IEC104_MONTH_OCT = 10,
+ IEC104_MONTH_NOV = 11,
+ IEC104_MONTH_DEC = 12,
+};
+
+enum Iec104WeekdayEnum
+{
+ IEC104_DAY_WEEKDAY_UNUSED = 0,
+ IEC104_DAY_MONDAY = 1,
+ IEC104_DAY_TUESDAY = 2,
+ IEC104_DAY_WEDNESDAY = 3,
+ IEC104_DAY_THURSDAY = 4,
+ IEC104_DAY_FRIDAY = 5,
+ IEC104_DAY_SATURDAY = 6,
+ IEC104_DAY_SUNDAY = 7,
+};
+
+enum Iec104CoiUiEnum
+{
+ IEC104_COI_UI_LOCPOWON = 0,
+ IEC104_COI_UI_LOCMANRST = 1,
+ IEC104_COI_UI_REMRST = 2,
+ IEC104_COI_UI_RES3 = 3,
+ IEC104_COI_UI_RES4 = 4,
+ IEC104_COI_UI_RES5 = 5,
+ IEC104_COI_UI_RES6 = 6,
+ IEC104_COI_UI_RES7 = 7,
+ IEC104_COI_UI_RES8 = 8,
+ IEC104_COI_UI_RES9 = 9,
+ IEC104_COI_UI_RES10 = 10,
+ IEC104_COI_UI_RES11 = 11,
+ IEC104_COI_UI_RES12 = 12,
+ IEC104_COI_UI_RES13 = 13,
+ IEC104_COI_UI_RES14 = 14,
+ IEC104_COI_UI_RES15 = 15,
+ IEC104_COI_UI_RES16 = 16,
+ IEC104_COI_UI_RES17 = 17,
+ IEC104_COI_UI_RES18 = 18,
+ IEC104_COI_UI_RES19 = 19,
+ IEC104_COI_UI_RES20 = 20,
+ IEC104_COI_UI_RES21 = 21,
+ IEC104_COI_UI_RES22 = 22,
+ IEC104_COI_UI_RES23 = 23,
+ IEC104_COI_UI_RES24 = 24,
+ IEC104_COI_UI_RES25 = 25,
+ IEC104_COI_UI_RES26 = 26,
+ IEC104_COI_UI_RES27 = 27,
+ IEC104_COI_UI_RES28 = 28,
+ IEC104_COI_UI_RES29 = 29,
+ IEC104_COI_UI_RES30 = 30,
+ IEC104_COI_UI_RES31 = 31,
+ IEC104_COI_UI_RES32 = 32,
+ IEC104_COI_UI_RES33 = 33,
+ IEC104_COI_UI_RES34 = 34,
+ IEC104_COI_UI_RES35 = 35,
+ IEC104_COI_UI_RES36 = 36,
+ IEC104_COI_UI_RES37 = 37,
+ IEC104_COI_UI_RES38 = 38,
+ IEC104_COI_UI_RES39 = 39,
+ IEC104_COI_UI_RES40 = 40,
+ IEC104_COI_UI_RES41 = 41,
+ IEC104_COI_UI_RES42 = 42,
+ IEC104_COI_UI_RES43 = 43,
+ IEC104_COI_UI_RES44 = 44,
+ IEC104_COI_UI_RES45 = 45,
+ IEC104_COI_UI_RES46 = 46,
+ IEC104_COI_UI_RES47 = 47,
+ IEC104_COI_UI_RES48 = 48,
+ IEC104_COI_UI_RES49 = 49,
+ IEC104_COI_UI_RES50 = 50,
+ IEC104_COI_UI_RES51 = 51,
+ IEC104_COI_UI_RES52 = 52,
+ IEC104_COI_UI_RES53 = 53,
+ IEC104_COI_UI_RES54 = 54,
+ IEC104_COI_UI_RES55 = 55,
+ IEC104_COI_UI_RES56 = 56,
+ IEC104_COI_UI_RES57 = 57,
+ IEC104_COI_UI_RES58 = 58,
+ IEC104_COI_UI_RES59 = 59,
+ IEC104_COI_UI_RES60 = 60,
+ IEC104_COI_UI_RES61 = 61,
+ IEC104_COI_UI_RES62 = 62,
+ IEC104_COI_UI_RES63 = 63,
+ IEC104_COI_UI_RES64 = 64,
+ IEC104_COI_UI_RES65 = 65,
+ IEC104_COI_UI_RES66 = 66,
+ IEC104_COI_UI_RES67 = 67,
+ IEC104_COI_UI_RES68 = 68,
+ IEC104_COI_UI_RES69 = 69,
+ IEC104_COI_UI_RES70 = 70,
+ IEC104_COI_UI_RES71 = 71,
+ IEC104_COI_UI_RES72 = 72,
+ IEC104_COI_UI_RES73 = 73,
+ IEC104_COI_UI_RES74 = 74,
+ IEC104_COI_UI_RES75 = 75,
+ IEC104_COI_UI_RES76 = 76,
+ IEC104_COI_UI_RES77 = 77,
+ IEC104_COI_UI_RES78 = 78,
+ IEC104_COI_UI_RES79 = 79,
+ IEC104_COI_UI_RES80 = 80,
+ IEC104_COI_UI_RES81 = 81,
+ IEC104_COI_UI_RES82 = 82,
+ IEC104_COI_UI_RES83 = 83,
+ IEC104_COI_UI_RES84 = 84,
+ IEC104_COI_UI_RES85 = 85,
+ IEC104_COI_UI_RES86 = 86,
+ IEC104_COI_UI_RES87 = 87,
+ IEC104_COI_UI_RES88 = 88,
+ IEC104_COI_UI_RES89 = 89,
+ IEC104_COI_UI_RES90 = 90,
+ IEC104_COI_UI_RES91 = 91,
+ IEC104_COI_UI_RES92 = 92,
+ IEC104_COI_UI_RES93 = 93,
+ IEC104_COI_UI_RES94 = 94,
+ IEC104_COI_UI_RES95 = 95,
+ IEC104_COI_UI_RES96 = 96,
+ IEC104_COI_UI_RES97 = 97,
+ IEC104_COI_UI_RES98 = 98,
+ IEC104_COI_UI_RES99 = 99,
+ IEC104_COI_UI_RES100 = 100,
+ IEC104_COI_UI_RES101 = 101,
+ IEC104_COI_UI_RES102 = 102,
+ IEC104_COI_UI_RES103 = 103,
+ IEC104_COI_UI_RES104 = 104,
+ IEC104_COI_UI_RES105 = 105,
+ IEC104_COI_UI_RES106 = 106,
+ IEC104_COI_UI_RES107 = 107,
+ IEC104_COI_UI_RES108 = 108,
+ IEC104_COI_UI_RES109 = 109,
+ IEC104_COI_UI_RES110 = 110,
+ IEC104_COI_UI_RES111 = 111,
+ IEC104_COI_UI_RES112 = 112,
+ IEC104_COI_UI_RES113 = 113,
+ IEC104_COI_UI_RES114 = 114,
+ IEC104_COI_UI_RES115 = 115,
+ IEC104_COI_UI_RES116 = 116,
+ IEC104_COI_UI_RES117 = 117,
+ IEC104_COI_UI_RES118 = 118,
+ IEC104_COI_UI_RES119 = 119,
+ IEC104_COI_UI_RES120 = 120,
+ IEC104_COI_UI_RES121 = 121,
+ IEC104_COI_UI_RES122 = 122,
+ IEC104_COI_UI_RES123 = 123,
+ IEC104_COI_UI_RES124 = 124,
+ IEC104_COI_UI_RES125 = 125,
+ IEC104_COI_UI_RES126 = 126,
+ IEC104_COI_UI_RES127 = 127,
+};
+
+enum Iec104QoiEnum
+{
+ IEC104_QOI_NOTUSED = 0,
+ IEC104_QOI_RES1 = 1,
+ IEC104_QOI_RES2 = 2,
+ IEC104_QOI_RES3 = 3,
+ IEC104_QOI_RES4 = 4,
+ IEC104_QOI_RES5 = 5,
+ IEC104_QOI_RES6 = 6,
+ IEC104_QOI_RES7 = 7,
+ IEC104_QOI_RES8 = 8,
+ IEC104_QOI_RES9 = 9,
+ IEC104_QOI_RES10 = 10,
+ IEC104_QOI_RES11 = 11,
+ IEC104_QOI_RES12 = 12,
+ IEC104_QOI_RES13 = 13,
+ IEC104_QOI_RES14 = 14,
+ IEC104_QOI_RES15 = 15,
+ IEC104_QOI_RES16 = 16,
+ IEC104_QOI_RES17 = 17,
+ IEC104_QOI_RES18 = 18,
+ IEC104_QOI_RES19 = 19,
+ IEC104_QOI_INROSTAT = 20,
+ IEC104_QOI_INRO1 = 21,
+ IEC104_QOI_INRO2 = 22,
+ IEC104_QOI_INRO3 = 23,
+ IEC104_QOI_INRO4 = 24,
+ IEC104_QOI_INRO5 = 25,
+ IEC104_QOI_INRO6 = 26,
+ IEC104_QOI_INRO7 = 27,
+ IEC104_QOI_INRO8 = 28,
+ IEC104_QOI_INRO9 = 29,
+ IEC104_QOI_INRO10 = 30,
+ IEC104_QOI_INRO11 = 31,
+ IEC104_QOI_INRO12 = 32,
+ IEC104_QOI_INRO13 = 33,
+ IEC104_QOI_INRO14 = 34,
+ IEC104_QOI_INRO15 = 35,
+ IEC104_QOI_INRO16 = 36,
+ IEC104_QOI_RES37 = 37,
+ IEC104_QOI_RES38 = 38,
+ IEC104_QOI_RES39 = 39,
+ IEC104_QOI_RES40 = 40,
+ IEC104_QOI_RES41 = 41,
+ IEC104_QOI_RES42 = 42,
+ IEC104_QOI_RES43 = 43,
+ IEC104_QOI_RES44 = 44,
+ IEC104_QOI_RES45 = 45,
+ IEC104_QOI_RES46 = 46,
+ IEC104_QOI_RES47 = 47,
+ IEC104_QOI_RES48 = 48,
+ IEC104_QOI_RES49 = 49,
+ IEC104_QOI_RES50 = 50,
+ IEC104_QOI_RES51 = 51,
+ IEC104_QOI_RES52 = 52,
+ IEC104_QOI_RES53 = 53,
+ IEC104_QOI_RES54 = 54,
+ IEC104_QOI_RES55 = 55,
+ IEC104_QOI_RES56 = 56,
+ IEC104_QOI_RES57 = 57,
+ IEC104_QOI_RES58 = 58,
+ IEC104_QOI_RES59 = 59,
+ IEC104_QOI_RES60 = 60,
+ IEC104_QOI_RES61 = 61,
+ IEC104_QOI_RES62 = 62,
+ IEC104_QOI_RES63 = 63,
+ IEC104_QOI_RES64 = 64,
+ IEC104_QOI_RES65 = 65,
+ IEC104_QOI_RES66 = 66,
+ IEC104_QOI_RES67 = 67,
+ IEC104_QOI_RES68 = 68,
+ IEC104_QOI_RES69 = 69,
+ IEC104_QOI_RES70 = 70,
+ IEC104_QOI_RES71 = 71,
+ IEC104_QOI_RES72 = 72,
+ IEC104_QOI_RES73 = 73,
+ IEC104_QOI_RES74 = 74,
+ IEC104_QOI_RES75 = 75,
+ IEC104_QOI_RES76 = 76,
+ IEC104_QOI_RES77 = 77,
+ IEC104_QOI_RES78 = 78,
+ IEC104_QOI_RES79 = 79,
+ IEC104_QOI_RES80 = 80,
+ IEC104_QOI_RES81 = 81,
+ IEC104_QOI_RES82 = 82,
+ IEC104_QOI_RES83 = 83,
+ IEC104_QOI_RES84 = 84,
+ IEC104_QOI_RES85 = 85,
+ IEC104_QOI_RES86 = 86,
+ IEC104_QOI_RES87 = 87,
+ IEC104_QOI_RES88 = 88,
+ IEC104_QOI_RES89 = 89,
+ IEC104_QOI_RES90 = 90,
+ IEC104_QOI_RES91 = 91,
+ IEC104_QOI_RES92 = 92,
+ IEC104_QOI_RES93 = 93,
+ IEC104_QOI_RES94 = 94,
+ IEC104_QOI_RES95 = 95,
+ IEC104_QOI_RES96 = 96,
+ IEC104_QOI_RES97 = 97,
+ IEC104_QOI_RES98 = 98,
+ IEC104_QOI_RES99 = 99,
+ IEC104_QOI_RES100 = 100,
+ IEC104_QOI_RES101 = 101,
+ IEC104_QOI_RES102 = 102,
+ IEC104_QOI_RES103 = 103,
+ IEC104_QOI_RES104 = 104,
+ IEC104_QOI_RES105 = 105,
+ IEC104_QOI_RES106 = 106,
+ IEC104_QOI_RES107 = 107,
+ IEC104_QOI_RES108 = 108,
+ IEC104_QOI_RES109 = 109,
+ IEC104_QOI_RES110 = 110,
+ IEC104_QOI_RES111 = 111,
+ IEC104_QOI_RES112 = 112,
+ IEC104_QOI_RES113 = 113,
+ IEC104_QOI_RES114 = 114,
+ IEC104_QOI_RES115 = 115,
+ IEC104_QOI_RES116 = 116,
+ IEC104_QOI_RES117 = 117,
+ IEC104_QOI_RES118 = 118,
+ IEC104_QOI_RES119 = 119,
+ IEC104_QOI_RES120 = 120,
+ IEC104_QOI_RES121 = 121,
+ IEC104_QOI_RES122 = 122,
+ IEC104_QOI_RES123 = 123,
+ IEC104_QOI_RES124 = 124,
+ IEC104_QOI_RES125 = 125,
+ IEC104_QOI_RES126 = 126,
+ IEC104_QOI_RES127 = 127,
+ IEC104_QOI_RES128 = 128,
+ IEC104_QOI_RES129 = 129,
+ IEC104_QOI_RES130 = 130,
+ IEC104_QOI_RES131 = 131,
+ IEC104_QOI_RES132 = 132,
+ IEC104_QOI_RES133 = 133,
+ IEC104_QOI_RES134 = 134,
+ IEC104_QOI_RES135 = 135,
+ IEC104_QOI_RES136 = 136,
+ IEC104_QOI_RES137 = 137,
+ IEC104_QOI_RES138 = 138,
+ IEC104_QOI_RES139 = 139,
+ IEC104_QOI_RES140 = 140,
+ IEC104_QOI_RES141 = 141,
+ IEC104_QOI_RES142 = 142,
+ IEC104_QOI_RES143 = 143,
+ IEC104_QOI_RES144 = 144,
+ IEC104_QOI_RES145 = 145,
+ IEC104_QOI_RES146 = 146,
+ IEC104_QOI_RES147 = 147,
+ IEC104_QOI_RES148 = 148,
+ IEC104_QOI_RES149 = 149,
+ IEC104_QOI_RES150 = 150,
+ IEC104_QOI_RES151 = 151,
+ IEC104_QOI_RES152 = 152,
+ IEC104_QOI_RES153 = 153,
+ IEC104_QOI_RES154 = 154,
+ IEC104_QOI_RES155 = 155,
+ IEC104_QOI_RES156 = 156,
+ IEC104_QOI_RES157 = 157,
+ IEC104_QOI_RES158 = 158,
+ IEC104_QOI_RES159 = 159,
+ IEC104_QOI_RES160 = 160,
+ IEC104_QOI_RES161 = 161,
+ IEC104_QOI_RES162 = 162,
+ IEC104_QOI_RES163 = 163,
+ IEC104_QOI_RES164 = 164,
+ IEC104_QOI_RES165 = 165,
+ IEC104_QOI_RES166 = 166,
+ IEC104_QOI_RES167 = 167,
+ IEC104_QOI_RES168 = 168,
+ IEC104_QOI_RES169 = 169,
+ IEC104_QOI_RES170 = 170,
+ IEC104_QOI_RES171 = 171,
+ IEC104_QOI_RES172 = 172,
+ IEC104_QOI_RES173 = 173,
+ IEC104_QOI_RES174 = 174,
+ IEC104_QOI_RES175 = 175,
+ IEC104_QOI_RES176 = 176,
+ IEC104_QOI_RES177 = 177,
+ IEC104_QOI_RES178 = 178,
+ IEC104_QOI_RES179 = 179,
+ IEC104_QOI_RES180 = 180,
+ IEC104_QOI_RES181 = 181,
+ IEC104_QOI_RES182 = 182,
+ IEC104_QOI_RES183 = 183,
+ IEC104_QOI_RES184 = 184,
+ IEC104_QOI_RES185 = 185,
+ IEC104_QOI_RES186 = 186,
+ IEC104_QOI_RES187 = 187,
+ IEC104_QOI_RES188 = 188,
+ IEC104_QOI_RES189 = 189,
+ IEC104_QOI_RES190 = 190,
+ IEC104_QOI_RES191 = 191,
+ IEC104_QOI_RES192 = 192,
+ IEC104_QOI_RES193 = 193,
+ IEC104_QOI_RES194 = 194,
+ IEC104_QOI_RES195 = 195,
+ IEC104_QOI_RES196 = 196,
+ IEC104_QOI_RES197 = 197,
+ IEC104_QOI_RES198 = 198,
+ IEC104_QOI_RES199 = 199,
+ IEC104_QOI_RES200 = 200,
+ IEC104_QOI_RES201 = 201,
+ IEC104_QOI_RES202 = 202,
+ IEC104_QOI_RES203 = 203,
+ IEC104_QOI_RES204 = 204,
+ IEC104_QOI_RES205 = 205,
+ IEC104_QOI_RES206 = 206,
+ IEC104_QOI_RES207 = 207,
+ IEC104_QOI_RES208 = 208,
+ IEC104_QOI_RES209 = 209,
+ IEC104_QOI_RES210 = 210,
+ IEC104_QOI_RES211 = 211,
+ IEC104_QOI_RES212 = 212,
+ IEC104_QOI_RES213 = 213,
+ IEC104_QOI_RES214 = 214,
+ IEC104_QOI_RES215 = 215,
+ IEC104_QOI_RES216 = 216,
+ IEC104_QOI_RES217 = 217,
+ IEC104_QOI_RES218 = 218,
+ IEC104_QOI_RES219 = 219,
+ IEC104_QOI_RES220 = 220,
+ IEC104_QOI_RES221 = 221,
+ IEC104_QOI_RES222 = 222,
+ IEC104_QOI_RES223 = 223,
+ IEC104_QOI_RES224 = 224,
+ IEC104_QOI_RES225 = 225,
+ IEC104_QOI_RES226 = 226,
+ IEC104_QOI_RES227 = 227,
+ IEC104_QOI_RES228 = 228,
+ IEC104_QOI_RES229 = 229,
+ IEC104_QOI_RES230 = 230,
+ IEC104_QOI_RES231 = 231,
+ IEC104_QOI_RES232 = 232,
+ IEC104_QOI_RES233 = 233,
+ IEC104_QOI_RES234 = 234,
+ IEC104_QOI_RES235 = 235,
+ IEC104_QOI_RES236 = 236,
+ IEC104_QOI_RES237 = 237,
+ IEC104_QOI_RES238 = 238,
+ IEC104_QOI_RES239 = 239,
+ IEC104_QOI_RES240 = 240,
+ IEC104_QOI_RES241 = 241,
+ IEC104_QOI_RES242 = 242,
+ IEC104_QOI_RES243 = 243,
+ IEC104_QOI_RES244 = 244,
+ IEC104_QOI_RES245 = 245,
+ IEC104_QOI_RES246 = 246,
+ IEC104_QOI_RES247 = 247,
+ IEC104_QOI_RES248 = 248,
+ IEC104_QOI_RES249 = 249,
+ IEC104_QOI_RES250 = 250,
+ IEC104_QOI_RES251 = 251,
+ IEC104_QOI_RES252 = 252,
+ IEC104_QOI_RES253 = 253,
+ IEC104_QOI_RES254 = 254,
+ IEC104_QOI_RES255 = 255,
+};
+
+enum Iec104QccRqtEnum
+{
+ IEC104_QCC_RQT_NOTUSED = 0,
+ IEC104_QCC_RQT_GROUP1 = 1,
+ IEC104_QCC_RQT_GROUP2 = 2,
+ IEC104_QCC_RQT_GROUP3 = 3,
+ IEC104_QCC_RQT_GROUP4 = 4,
+ IEC104_QCC_RQT_GENCTR = 5,
+ IEC104_QCC_RQT_RES6 = 6,
+ IEC104_QCC_RQT_RES7 = 7,
+ IEC104_QCC_RQT_RES8 = 8,
+ IEC104_QCC_RQT_RES9 = 9,
+ IEC104_QCC_RQT_RES10 = 10,
+ IEC104_QCC_RQT_RES11 = 11,
+ IEC104_QCC_RQT_RES12 = 12,
+ IEC104_QCC_RQT_RES13 = 13,
+ IEC104_QCC_RQT_RES14 = 14,
+ IEC104_QCC_RQT_RES15 = 15,
+ IEC104_QCC_RQT_RES16 = 16,
+ IEC104_QCC_RQT_RES17 = 17,
+ IEC104_QCC_RQT_RES18 = 18,
+ IEC104_QCC_RQT_RES19 = 19,
+ IEC104_QCC_RQT_RES20 = 20,
+ IEC104_QCC_RQT_RES21 = 21,
+ IEC104_QCC_RQT_RES22 = 22,
+ IEC104_QCC_RQT_RES23 = 23,
+ IEC104_QCC_RQT_RES24 = 24,
+ IEC104_QCC_RQT_RES25 = 25,
+ IEC104_QCC_RQT_RES26 = 26,
+ IEC104_QCC_RQT_RES27 = 27,
+ IEC104_QCC_RQT_RES28 = 28,
+ IEC104_QCC_RQT_RES29 = 29,
+ IEC104_QCC_RQT_RES30 = 30,
+ IEC104_QCC_RQT_RES31 = 31,
+ IEC104_QCC_RQT_RES32 = 32,
+ IEC104_QCC_RQT_RES33 = 33,
+ IEC104_QCC_RQT_RES34 = 34,
+ IEC104_QCC_RQT_RES35 = 35,
+ IEC104_QCC_RQT_RES36 = 36,
+ IEC104_QCC_RQT_RES37 = 37,
+ IEC104_QCC_RQT_RES38 = 38,
+ IEC104_QCC_RQT_RES39 = 39,
+ IEC104_QCC_RQT_RES40 = 40,
+ IEC104_QCC_RQT_RES41 = 41,
+ IEC104_QCC_RQT_RES42 = 42,
+ IEC104_QCC_RQT_RES43 = 43,
+ IEC104_QCC_RQT_RES44 = 44,
+ IEC104_QCC_RQT_RES45 = 45,
+ IEC104_QCC_RQT_RES46 = 46,
+ IEC104_QCC_RQT_RES47 = 47,
+ IEC104_QCC_RQT_RES48 = 48,
+ IEC104_QCC_RQT_RES49 = 49,
+ IEC104_QCC_RQT_RES50 = 50,
+ IEC104_QCC_RQT_RES51 = 51,
+ IEC104_QCC_RQT_RES52 = 52,
+ IEC104_QCC_RQT_RES53 = 53,
+ IEC104_QCC_RQT_RES54 = 54,
+ IEC104_QCC_RQT_RES55 = 55,
+ IEC104_QCC_RQT_RES56 = 56,
+ IEC104_QCC_RQT_RES57 = 57,
+ IEC104_QCC_RQT_RES58 = 58,
+ IEC104_QCC_RQT_RES59 = 59,
+ IEC104_QCC_RQT_RES60 = 60,
+ IEC104_QCC_RQT_RES61 = 61,
+ IEC104_QCC_RQT_RES62 = 62,
+ IEC104_QCC_RQT_RES63 = 63,
+};
+
+enum Iec104QccFrzEnum
+{
+ IEC104_QCC_FRZ_READ = 0,
+ IEC104_QCC_FRZ_CTRFRZWITHOUTRST = 1,
+ IEC104_QCC_FRZ_CTRFRZWITHRST = 2,
+ IEC104_QCC_FRZ_CTRRST = 3,
+};
+
+enum Iec104QpmKpaEnum
+{
+ IEC104_QPM_KPA_NOTUSED = 0,
+ IEC104_QPM_KPA_THRESHVAL = 1,
+ IEC104_QPM_KPA_SMOOTHFACTOR = 2,
+ IEC104_QPM_KPA_LOWTXLMT = 3,
+ IEC104_QPM_KPA_HIGHTXLMT = 4,
+ IEC104_QPM_KPA_RES5 = 5,
+ IEC104_QPM_KPA_RES6 = 6,
+ IEC104_QPM_KPA_RES7 = 7,
+ IEC104_QPM_KPA_RES8 = 8,
+ IEC104_QPM_KPA_RES9 = 9,
+ IEC104_QPM_KPA_RES10 = 10,
+ IEC104_QPM_KPA_RES11 = 11,
+ IEC104_QPM_KPA_RES12 = 12,
+ IEC104_QPM_KPA_RES13 = 13,
+ IEC104_QPM_KPA_RES14 = 14,
+ IEC104_QPM_KPA_RES15 = 15,
+ IEC104_QPM_KPA_RES16 = 16,
+ IEC104_QPM_KPA_RES17 = 17,
+ IEC104_QPM_KPA_RES18 = 18,
+ IEC104_QPM_KPA_RES19 = 19,
+ IEC104_QPM_KPA_RES20 = 20,
+ IEC104_QPM_KPA_RES21 = 21,
+ IEC104_QPM_KPA_RES22 = 22,
+ IEC104_QPM_KPA_RES23 = 23,
+ IEC104_QPM_KPA_RES24 = 24,
+ IEC104_QPM_KPA_RES25 = 25,
+ IEC104_QPM_KPA_RES26 = 26,
+ IEC104_QPM_KPA_RES27 = 27,
+ IEC104_QPM_KPA_RES28 = 28,
+ IEC104_QPM_KPA_RES29 = 29,
+ IEC104_QPM_KPA_RES30 = 30,
+ IEC104_QPM_KPA_RES31 = 31,
+ IEC104_QPM_KPA_RES32 = 32,
+ IEC104_QPM_KPA_RES33 = 33,
+ IEC104_QPM_KPA_RES34 = 34,
+ IEC104_QPM_KPA_RES35 = 35,
+ IEC104_QPM_KPA_RES36 = 36,
+ IEC104_QPM_KPA_RES37 = 37,
+ IEC104_QPM_KPA_RES38 = 38,
+ IEC104_QPM_KPA_RES39 = 39,
+ IEC104_QPM_KPA_RES40 = 40,
+ IEC104_QPM_KPA_RES41 = 41,
+ IEC104_QPM_KPA_RES42 = 42,
+ IEC104_QPM_KPA_RES43 = 43,
+ IEC104_QPM_KPA_RES44 = 44,
+ IEC104_QPM_KPA_RES45 = 45,
+ IEC104_QPM_KPA_RES46 = 46,
+ IEC104_QPM_KPA_RES47 = 47,
+ IEC104_QPM_KPA_RES48 = 48,
+ IEC104_QPM_KPA_RES49 = 49,
+ IEC104_QPM_KPA_RES50 = 50,
+ IEC104_QPM_KPA_RES51 = 51,
+ IEC104_QPM_KPA_RES52 = 52,
+ IEC104_QPM_KPA_RES53 = 53,
+ IEC104_QPM_KPA_RES54 = 54,
+ IEC104_QPM_KPA_RES55 = 55,
+ IEC104_QPM_KPA_RES56 = 56,
+ IEC104_QPM_KPA_RES57 = 57,
+ IEC104_QPM_KPA_RES58 = 58,
+ IEC104_QPM_KPA_RES59 = 59,
+ IEC104_QPM_KPA_RES60 = 60,
+ IEC104_QPM_KPA_RES61 = 61,
+ IEC104_QPM_KPA_RES62 = 62,
+ IEC104_QPM_KPA_RES63 = 63,
+};
+
+enum Iec104QpaEnum
+{
+ IEC104_QPA_NOTUSED = 0,
+ IEC104_QPA_ACTDEACTPREVPARAM = 1,
+ IEC104_QPA_ACTDEACTPARAM = 2,
+ IEC104_QPA_ACTDEACTCYCTX = 3,
+ IEC104_QPA_RES4 = 4,
+ IEC104_QPA_RES5 = 5,
+ IEC104_QPA_RES6 = 6,
+ IEC104_QPA_RES7 = 7,
+ IEC104_QPA_RES8 = 8,
+ IEC104_QPA_RES9 = 9,
+ IEC104_QPA_RES10 = 10,
+ IEC104_QPA_RES11 = 11,
+ IEC104_QPA_RES12 = 12,
+ IEC104_QPA_RES13 = 13,
+ IEC104_QPA_RES14 = 14,
+ IEC104_QPA_RES15 = 15,
+ IEC104_QPA_RES16 = 16,
+ IEC104_QPA_RES17 = 17,
+ IEC104_QPA_RES18 = 18,
+ IEC104_QPA_RES19 = 19,
+ IEC104_QPA_RES20 = 20,
+ IEC104_QPA_RES21 = 21,
+ IEC104_QPA_RES22 = 22,
+ IEC104_QPA_RES23 = 23,
+ IEC104_QPA_RES24 = 24,
+ IEC104_QPA_RES25 = 25,
+ IEC104_QPA_RES26 = 26,
+ IEC104_QPA_RES27 = 27,
+ IEC104_QPA_RES28 = 28,
+ IEC104_QPA_RES29 = 29,
+ IEC104_QPA_RES30 = 30,
+ IEC104_QPA_RES31 = 31,
+ IEC104_QPA_RES32 = 32,
+ IEC104_QPA_RES33 = 33,
+ IEC104_QPA_RES34 = 34,
+ IEC104_QPA_RES35 = 35,
+ IEC104_QPA_RES36 = 36,
+ IEC104_QPA_RES37 = 37,
+ IEC104_QPA_RES38 = 38,
+ IEC104_QPA_RES39 = 39,
+ IEC104_QPA_RES40 = 40,
+ IEC104_QPA_RES41 = 41,
+ IEC104_QPA_RES42 = 42,
+ IEC104_QPA_RES43 = 43,
+ IEC104_QPA_RES44 = 44,
+ IEC104_QPA_RES45 = 45,
+ IEC104_QPA_RES46 = 46,
+ IEC104_QPA_RES47 = 47,
+ IEC104_QPA_RES48 = 48,
+ IEC104_QPA_RES49 = 49,
+ IEC104_QPA_RES50 = 50,
+ IEC104_QPA_RES51 = 51,
+ IEC104_QPA_RES52 = 52,
+ IEC104_QPA_RES53 = 53,
+ IEC104_QPA_RES54 = 54,
+ IEC104_QPA_RES55 = 55,
+ IEC104_QPA_RES56 = 56,
+ IEC104_QPA_RES57 = 57,
+ IEC104_QPA_RES58 = 58,
+ IEC104_QPA_RES59 = 59,
+ IEC104_QPA_RES60 = 60,
+ IEC104_QPA_RES61 = 61,
+ IEC104_QPA_RES62 = 62,
+ IEC104_QPA_RES63 = 63,
+ IEC104_QPA_RES64 = 64,
+ IEC104_QPA_RES65 = 65,
+ IEC104_QPA_RES66 = 66,
+ IEC104_QPA_RES67 = 67,
+ IEC104_QPA_RES68 = 68,
+ IEC104_QPA_RES69 = 69,
+ IEC104_QPA_RES70 = 70,
+ IEC104_QPA_RES71 = 71,
+ IEC104_QPA_RES72 = 72,
+ IEC104_QPA_RES73 = 73,
+ IEC104_QPA_RES74 = 74,
+ IEC104_QPA_RES75 = 75,
+ IEC104_QPA_RES76 = 76,
+ IEC104_QPA_RES77 = 77,
+ IEC104_QPA_RES78 = 78,
+ IEC104_QPA_RES79 = 79,
+ IEC104_QPA_RES80 = 80,
+ IEC104_QPA_RES81 = 81,
+ IEC104_QPA_RES82 = 82,
+ IEC104_QPA_RES83 = 83,
+ IEC104_QPA_RES84 = 84,
+ IEC104_QPA_RES85 = 85,
+ IEC104_QPA_RES86 = 86,
+ IEC104_QPA_RES87 = 87,
+ IEC104_QPA_RES88 = 88,
+ IEC104_QPA_RES89 = 89,
+ IEC104_QPA_RES90 = 90,
+ IEC104_QPA_RES91 = 91,
+ IEC104_QPA_RES92 = 92,
+ IEC104_QPA_RES93 = 93,
+ IEC104_QPA_RES94 = 94,
+ IEC104_QPA_RES95 = 95,
+ IEC104_QPA_RES96 = 96,
+ IEC104_QPA_RES97 = 97,
+ IEC104_QPA_RES98 = 98,
+ IEC104_QPA_RES99 = 99,
+ IEC104_QPA_RES100 = 100,
+ IEC104_QPA_RES101 = 101,
+ IEC104_QPA_RES102 = 102,
+ IEC104_QPA_RES103 = 103,
+ IEC104_QPA_RES104 = 104,
+ IEC104_QPA_RES105 = 105,
+ IEC104_QPA_RES106 = 106,
+ IEC104_QPA_RES107 = 107,
+ IEC104_QPA_RES108 = 108,
+ IEC104_QPA_RES109 = 109,
+ IEC104_QPA_RES110 = 110,
+ IEC104_QPA_RES111 = 111,
+ IEC104_QPA_RES112 = 112,
+ IEC104_QPA_RES113 = 113,
+ IEC104_QPA_RES114 = 114,
+ IEC104_QPA_RES115 = 115,
+ IEC104_QPA_RES116 = 116,
+ IEC104_QPA_RES117 = 117,
+ IEC104_QPA_RES118 = 118,
+ IEC104_QPA_RES119 = 119,
+ IEC104_QPA_RES120 = 120,
+ IEC104_QPA_RES121 = 121,
+ IEC104_QPA_RES122 = 122,
+ IEC104_QPA_RES123 = 123,
+ IEC104_QPA_RES124 = 124,
+ IEC104_QPA_RES125 = 125,
+ IEC104_QPA_RES126 = 126,
+ IEC104_QPA_RES127 = 127,
+ IEC104_QPA_RES128 = 128,
+ IEC104_QPA_RES129 = 129,
+ IEC104_QPA_RES130 = 130,
+ IEC104_QPA_RES131 = 131,
+ IEC104_QPA_RES132 = 132,
+ IEC104_QPA_RES133 = 133,
+ IEC104_QPA_RES134 = 134,
+ IEC104_QPA_RES135 = 135,
+ IEC104_QPA_RES136 = 136,
+ IEC104_QPA_RES137 = 137,
+ IEC104_QPA_RES138 = 138,
+ IEC104_QPA_RES139 = 139,
+ IEC104_QPA_RES140 = 140,
+ IEC104_QPA_RES141 = 141,
+ IEC104_QPA_RES142 = 142,
+ IEC104_QPA_RES143 = 143,
+ IEC104_QPA_RES144 = 144,
+ IEC104_QPA_RES145 = 145,
+ IEC104_QPA_RES146 = 146,
+ IEC104_QPA_RES147 = 147,
+ IEC104_QPA_RES148 = 148,
+ IEC104_QPA_RES149 = 149,
+ IEC104_QPA_RES150 = 150,
+ IEC104_QPA_RES151 = 151,
+ IEC104_QPA_RES152 = 152,
+ IEC104_QPA_RES153 = 153,
+ IEC104_QPA_RES154 = 154,
+ IEC104_QPA_RES155 = 155,
+ IEC104_QPA_RES156 = 156,
+ IEC104_QPA_RES157 = 157,
+ IEC104_QPA_RES158 = 158,
+ IEC104_QPA_RES159 = 159,
+ IEC104_QPA_RES160 = 160,
+ IEC104_QPA_RES161 = 161,
+ IEC104_QPA_RES162 = 162,
+ IEC104_QPA_RES163 = 163,
+ IEC104_QPA_RES164 = 164,
+ IEC104_QPA_RES165 = 165,
+ IEC104_QPA_RES166 = 166,
+ IEC104_QPA_RES167 = 167,
+ IEC104_QPA_RES168 = 168,
+ IEC104_QPA_RES169 = 169,
+ IEC104_QPA_RES170 = 170,
+ IEC104_QPA_RES171 = 171,
+ IEC104_QPA_RES172 = 172,
+ IEC104_QPA_RES173 = 173,
+ IEC104_QPA_RES174 = 174,
+ IEC104_QPA_RES175 = 175,
+ IEC104_QPA_RES176 = 176,
+ IEC104_QPA_RES177 = 177,
+ IEC104_QPA_RES178 = 178,
+ IEC104_QPA_RES179 = 179,
+ IEC104_QPA_RES180 = 180,
+ IEC104_QPA_RES181 = 181,
+ IEC104_QPA_RES182 = 182,
+ IEC104_QPA_RES183 = 183,
+ IEC104_QPA_RES184 = 184,
+ IEC104_QPA_RES185 = 185,
+ IEC104_QPA_RES186 = 186,
+ IEC104_QPA_RES187 = 187,
+ IEC104_QPA_RES188 = 188,
+ IEC104_QPA_RES189 = 189,
+ IEC104_QPA_RES190 = 190,
+ IEC104_QPA_RES191 = 191,
+ IEC104_QPA_RES192 = 192,
+ IEC104_QPA_RES193 = 193,
+ IEC104_QPA_RES194 = 194,
+ IEC104_QPA_RES195 = 195,
+ IEC104_QPA_RES196 = 196,
+ IEC104_QPA_RES197 = 197,
+ IEC104_QPA_RES198 = 198,
+ IEC104_QPA_RES199 = 199,
+ IEC104_QPA_RES200 = 200,
+ IEC104_QPA_RES201 = 201,
+ IEC104_QPA_RES202 = 202,
+ IEC104_QPA_RES203 = 203,
+ IEC104_QPA_RES204 = 204,
+ IEC104_QPA_RES205 = 205,
+ IEC104_QPA_RES206 = 206,
+ IEC104_QPA_RES207 = 207,
+ IEC104_QPA_RES208 = 208,
+ IEC104_QPA_RES209 = 209,
+ IEC104_QPA_RES210 = 210,
+ IEC104_QPA_RES211 = 211,
+ IEC104_QPA_RES212 = 212,
+ IEC104_QPA_RES213 = 213,
+ IEC104_QPA_RES214 = 214,
+ IEC104_QPA_RES215 = 215,
+ IEC104_QPA_RES216 = 216,
+ IEC104_QPA_RES217 = 217,
+ IEC104_QPA_RES218 = 218,
+ IEC104_QPA_RES219 = 219,
+ IEC104_QPA_RES220 = 220,
+ IEC104_QPA_RES221 = 221,
+ IEC104_QPA_RES222 = 222,
+ IEC104_QPA_RES223 = 223,
+ IEC104_QPA_RES224 = 224,
+ IEC104_QPA_RES225 = 225,
+ IEC104_QPA_RES226 = 226,
+ IEC104_QPA_RES227 = 227,
+ IEC104_QPA_RES228 = 228,
+ IEC104_QPA_RES229 = 229,
+ IEC104_QPA_RES230 = 230,
+ IEC104_QPA_RES231 = 231,
+ IEC104_QPA_RES232 = 232,
+ IEC104_QPA_RES233 = 233,
+ IEC104_QPA_RES234 = 234,
+ IEC104_QPA_RES235 = 235,
+ IEC104_QPA_RES236 = 236,
+ IEC104_QPA_RES237 = 237,
+ IEC104_QPA_RES238 = 238,
+ IEC104_QPA_RES239 = 239,
+ IEC104_QPA_RES240 = 240,
+ IEC104_QPA_RES241 = 241,
+ IEC104_QPA_RES242 = 242,
+ IEC104_QPA_RES243 = 243,
+ IEC104_QPA_RES244 = 244,
+ IEC104_QPA_RES245 = 245,
+ IEC104_QPA_RES246 = 246,
+ IEC104_QPA_RES247 = 247,
+ IEC104_QPA_RES248 = 248,
+ IEC104_QPA_RES249 = 249,
+ IEC104_QPA_RES250 = 250,
+ IEC104_QPA_RES251 = 251,
+ IEC104_QPA_RES252 = 252,
+ IEC104_QPA_RES253 = 253,
+ IEC104_QPA_RES254 = 254,
+ IEC104_QPA_RES255 = 255,
+};
+
+enum Iec104QocQuEnum
+{
+ IEC104_QOC_QU_NOADDDEF = 0,
+ IEC104_QOC_QU_SHORTPULSE = 1,
+ IEC104_QOC_QU_LONGPULSE = 2,
+ IEC104_QOC_QU_PERSIST = 3,
+ IEC104_QOC_QU_RES4 = 4,
+ IEC104_QOC_QU_RES5 = 5,
+ IEC104_QOC_QU_RES6 = 6,
+ IEC104_QOC_QU_RES7 = 7,
+ IEC104_QOC_QU_RES8 = 8,
+ IEC104_QOC_QU_RES9 = 9,
+ IEC104_QOC_QU_RES10 = 10,
+ IEC104_QOC_QU_RES11 = 11,
+ IEC104_QOC_QU_RES12 = 12,
+ IEC104_QOC_QU_RES13 = 13,
+ IEC104_QOC_QU_RES14 = 14,
+ IEC104_QOC_QU_RES15 = 15,
+ IEC104_QOC_QU_RES16 = 16,
+ IEC104_QOC_QU_RES17 = 17,
+ IEC104_QOC_QU_RES18 = 18,
+ IEC104_QOC_QU_RES19 = 19,
+ IEC104_QOC_QU_RES20 = 20,
+ IEC104_QOC_QU_RES21 = 21,
+ IEC104_QOC_QU_RES22 = 22,
+ IEC104_QOC_QU_RES23 = 23,
+ IEC104_QOC_QU_RES24 = 24,
+ IEC104_QOC_QU_RES25 = 25,
+ IEC104_QOC_QU_RES26 = 26,
+ IEC104_QOC_QU_RES27 = 27,
+ IEC104_QOC_QU_RES28 = 28,
+ IEC104_QOC_QU_RES29 = 29,
+ IEC104_QOC_QU_RES30 = 30,
+ IEC104_QOC_QU_RES31 = 31,
+};
+
+enum Iec104QrpEnum
+{
+ IEC104_QRP_NOTUSED = 0,
+ IEC104_QRP_GENRST = 1,
+ IEC104_QRP_RSTTIME = 2,
+ IEC104_QRP_RES3 = 3,
+ IEC104_QRP_RES4 = 4,
+ IEC104_QRP_RES5 = 5,
+ IEC104_QRP_RES6 = 6,
+ IEC104_QRP_RES7 = 7,
+ IEC104_QRP_RES8 = 8,
+ IEC104_QRP_RES9 = 9,
+ IEC104_QRP_RES10 = 10,
+ IEC104_QRP_RES11 = 11,
+ IEC104_QRP_RES12 = 12,
+ IEC104_QRP_RES13 = 13,
+ IEC104_QRP_RES14 = 14,
+ IEC104_QRP_RES15 = 15,
+ IEC104_QRP_RES16 = 16,
+ IEC104_QRP_RES17 = 17,
+ IEC104_QRP_RES18 = 18,
+ IEC104_QRP_RES19 = 19,
+ IEC104_QRP_RES20 = 20,
+ IEC104_QRP_RES21 = 21,
+ IEC104_QRP_RES22 = 22,
+ IEC104_QRP_RES23 = 23,
+ IEC104_QRP_RES24 = 24,
+ IEC104_QRP_RES25 = 25,
+ IEC104_QRP_RES26 = 26,
+ IEC104_QRP_RES27 = 27,
+ IEC104_QRP_RES28 = 28,
+ IEC104_QRP_RES29 = 29,
+ IEC104_QRP_RES30 = 30,
+ IEC104_QRP_RES31 = 31,
+ IEC104_QRP_RES32 = 32,
+ IEC104_QRP_RES33 = 33,
+ IEC104_QRP_RES34 = 34,
+ IEC104_QRP_RES35 = 35,
+ IEC104_QRP_RES36 = 36,
+ IEC104_QRP_RES37 = 37,
+ IEC104_QRP_RES38 = 38,
+ IEC104_QRP_RES39 = 39,
+ IEC104_QRP_RES40 = 40,
+ IEC104_QRP_RES41 = 41,
+ IEC104_QRP_RES42 = 42,
+ IEC104_QRP_RES43 = 43,
+ IEC104_QRP_RES44 = 44,
+ IEC104_QRP_RES45 = 45,
+ IEC104_QRP_RES46 = 46,
+ IEC104_QRP_RES47 = 47,
+ IEC104_QRP_RES48 = 48,
+ IEC104_QRP_RES49 = 49,
+ IEC104_QRP_RES50 = 50,
+ IEC104_QRP_RES51 = 51,
+ IEC104_QRP_RES52 = 52,
+ IEC104_QRP_RES53 = 53,
+ IEC104_QRP_RES54 = 54,
+ IEC104_QRP_RES55 = 55,
+ IEC104_QRP_RES56 = 56,
+ IEC104_QRP_RES57 = 57,
+ IEC104_QRP_RES58 = 58,
+ IEC104_QRP_RES59 = 59,
+ IEC104_QRP_RES60 = 60,
+ IEC104_QRP_RES61 = 61,
+ IEC104_QRP_RES62 = 62,
+ IEC104_QRP_RES63 = 63,
+ IEC104_QRP_RES64 = 64,
+ IEC104_QRP_RES65 = 65,
+ IEC104_QRP_RES66 = 66,
+ IEC104_QRP_RES67 = 67,
+ IEC104_QRP_RES68 = 68,
+ IEC104_QRP_RES69 = 69,
+ IEC104_QRP_RES70 = 70,
+ IEC104_QRP_RES71 = 71,
+ IEC104_QRP_RES72 = 72,
+ IEC104_QRP_RES73 = 73,
+ IEC104_QRP_RES74 = 74,
+ IEC104_QRP_RES75 = 75,
+ IEC104_QRP_RES76 = 76,
+ IEC104_QRP_RES77 = 77,
+ IEC104_QRP_RES78 = 78,
+ IEC104_QRP_RES79 = 79,
+ IEC104_QRP_RES80 = 80,
+ IEC104_QRP_RES81 = 81,
+ IEC104_QRP_RES82 = 82,
+ IEC104_QRP_RES83 = 83,
+ IEC104_QRP_RES84 = 84,
+ IEC104_QRP_RES85 = 85,
+ IEC104_QRP_RES86 = 86,
+ IEC104_QRP_RES87 = 87,
+ IEC104_QRP_RES88 = 88,
+ IEC104_QRP_RES89 = 89,
+ IEC104_QRP_RES90 = 90,
+ IEC104_QRP_RES91 = 91,
+ IEC104_QRP_RES92 = 92,
+ IEC104_QRP_RES93 = 93,
+ IEC104_QRP_RES94 = 94,
+ IEC104_QRP_RES95 = 95,
+ IEC104_QRP_RES96 = 96,
+ IEC104_QRP_RES97 = 97,
+ IEC104_QRP_RES98 = 98,
+ IEC104_QRP_RES99 = 99,
+ IEC104_QRP_RES100 = 100,
+ IEC104_QRP_RES101 = 101,
+ IEC104_QRP_RES102 = 102,
+ IEC104_QRP_RES103 = 103,
+ IEC104_QRP_RES104 = 104,
+ IEC104_QRP_RES105 = 105,
+ IEC104_QRP_RES106 = 106,
+ IEC104_QRP_RES107 = 107,
+ IEC104_QRP_RES108 = 108,
+ IEC104_QRP_RES109 = 109,
+ IEC104_QRP_RES110 = 110,
+ IEC104_QRP_RES111 = 111,
+ IEC104_QRP_RES112 = 112,
+ IEC104_QRP_RES113 = 113,
+ IEC104_QRP_RES114 = 114,
+ IEC104_QRP_RES115 = 115,
+ IEC104_QRP_RES116 = 116,
+ IEC104_QRP_RES117 = 117,
+ IEC104_QRP_RES118 = 118,
+ IEC104_QRP_RES119 = 119,
+ IEC104_QRP_RES120 = 120,
+ IEC104_QRP_RES121 = 121,
+ IEC104_QRP_RES122 = 122,
+ IEC104_QRP_RES123 = 123,
+ IEC104_QRP_RES124 = 124,
+ IEC104_QRP_RES125 = 125,
+ IEC104_QRP_RES126 = 126,
+ IEC104_QRP_RES127 = 127,
+ IEC104_QRP_RES128 = 128,
+ IEC104_QRP_RES129 = 129,
+ IEC104_QRP_RES130 = 130,
+ IEC104_QRP_RES131 = 131,
+ IEC104_QRP_RES132 = 132,
+ IEC104_QRP_RES133 = 133,
+ IEC104_QRP_RES134 = 134,
+ IEC104_QRP_RES135 = 135,
+ IEC104_QRP_RES136 = 136,
+ IEC104_QRP_RES137 = 137,
+ IEC104_QRP_RES138 = 138,
+ IEC104_QRP_RES139 = 139,
+ IEC104_QRP_RES140 = 140,
+ IEC104_QRP_RES141 = 141,
+ IEC104_QRP_RES142 = 142,
+ IEC104_QRP_RES143 = 143,
+ IEC104_QRP_RES144 = 144,
+ IEC104_QRP_RES145 = 145,
+ IEC104_QRP_RES146 = 146,
+ IEC104_QRP_RES147 = 147,
+ IEC104_QRP_RES148 = 148,
+ IEC104_QRP_RES149 = 149,
+ IEC104_QRP_RES150 = 150,
+ IEC104_QRP_RES151 = 151,
+ IEC104_QRP_RES152 = 152,
+ IEC104_QRP_RES153 = 153,
+ IEC104_QRP_RES154 = 154,
+ IEC104_QRP_RES155 = 155,
+ IEC104_QRP_RES156 = 156,
+ IEC104_QRP_RES157 = 157,
+ IEC104_QRP_RES158 = 158,
+ IEC104_QRP_RES159 = 159,
+ IEC104_QRP_RES160 = 160,
+ IEC104_QRP_RES161 = 161,
+ IEC104_QRP_RES162 = 162,
+ IEC104_QRP_RES163 = 163,
+ IEC104_QRP_RES164 = 164,
+ IEC104_QRP_RES165 = 165,
+ IEC104_QRP_RES166 = 166,
+ IEC104_QRP_RES167 = 167,
+ IEC104_QRP_RES168 = 168,
+ IEC104_QRP_RES169 = 169,
+ IEC104_QRP_RES170 = 170,
+ IEC104_QRP_RES171 = 171,
+ IEC104_QRP_RES172 = 172,
+ IEC104_QRP_RES173 = 173,
+ IEC104_QRP_RES174 = 174,
+ IEC104_QRP_RES175 = 175,
+ IEC104_QRP_RES176 = 176,
+ IEC104_QRP_RES177 = 177,
+ IEC104_QRP_RES178 = 178,
+ IEC104_QRP_RES179 = 179,
+ IEC104_QRP_RES180 = 180,
+ IEC104_QRP_RES181 = 181,
+ IEC104_QRP_RES182 = 182,
+ IEC104_QRP_RES183 = 183,
+ IEC104_QRP_RES184 = 184,
+ IEC104_QRP_RES185 = 185,
+ IEC104_QRP_RES186 = 186,
+ IEC104_QRP_RES187 = 187,
+ IEC104_QRP_RES188 = 188,
+ IEC104_QRP_RES189 = 189,
+ IEC104_QRP_RES190 = 190,
+ IEC104_QRP_RES191 = 191,
+ IEC104_QRP_RES192 = 192,
+ IEC104_QRP_RES193 = 193,
+ IEC104_QRP_RES194 = 194,
+ IEC104_QRP_RES195 = 195,
+ IEC104_QRP_RES196 = 196,
+ IEC104_QRP_RES197 = 197,
+ IEC104_QRP_RES198 = 198,
+ IEC104_QRP_RES199 = 199,
+ IEC104_QRP_RES200 = 200,
+ IEC104_QRP_RES201 = 201,
+ IEC104_QRP_RES202 = 202,
+ IEC104_QRP_RES203 = 203,
+ IEC104_QRP_RES204 = 204,
+ IEC104_QRP_RES205 = 205,
+ IEC104_QRP_RES206 = 206,
+ IEC104_QRP_RES207 = 207,
+ IEC104_QRP_RES208 = 208,
+ IEC104_QRP_RES209 = 209,
+ IEC104_QRP_RES210 = 210,
+ IEC104_QRP_RES211 = 211,
+ IEC104_QRP_RES212 = 212,
+ IEC104_QRP_RES213 = 213,
+ IEC104_QRP_RES214 = 214,
+ IEC104_QRP_RES215 = 215,
+ IEC104_QRP_RES216 = 216,
+ IEC104_QRP_RES217 = 217,
+ IEC104_QRP_RES218 = 218,
+ IEC104_QRP_RES219 = 219,
+ IEC104_QRP_RES220 = 220,
+ IEC104_QRP_RES221 = 221,
+ IEC104_QRP_RES222 = 222,
+ IEC104_QRP_RES223 = 223,
+ IEC104_QRP_RES224 = 224,
+ IEC104_QRP_RES225 = 225,
+ IEC104_QRP_RES226 = 226,
+ IEC104_QRP_RES227 = 227,
+ IEC104_QRP_RES228 = 228,
+ IEC104_QRP_RES229 = 229,
+ IEC104_QRP_RES230 = 230,
+ IEC104_QRP_RES231 = 231,
+ IEC104_QRP_RES232 = 232,
+ IEC104_QRP_RES233 = 233,
+ IEC104_QRP_RES234 = 234,
+ IEC104_QRP_RES235 = 235,
+ IEC104_QRP_RES236 = 236,
+ IEC104_QRP_RES237 = 237,
+ IEC104_QRP_RES238 = 238,
+ IEC104_QRP_RES239 = 239,
+ IEC104_QRP_RES240 = 240,
+ IEC104_QRP_RES241 = 241,
+ IEC104_QRP_RES242 = 242,
+ IEC104_QRP_RES243 = 243,
+ IEC104_QRP_RES244 = 244,
+ IEC104_QRP_RES245 = 245,
+ IEC104_QRP_RES246 = 246,
+ IEC104_QRP_RES247 = 247,
+ IEC104_QRP_RES248 = 248,
+ IEC104_QRP_RES249 = 249,
+ IEC104_QRP_RES250 = 250,
+ IEC104_QRP_RES251 = 251,
+ IEC104_QRP_RES252 = 252,
+ IEC104_QRP_RES253 = 253,
+ IEC104_QRP_RES254 = 254,
+ IEC104_QRP_RES255 = 255,
+};
+
+enum Iec104FrqUiEnum
+{
+ IEC104_FRQ_UI_DEFAULT = 0,
+ IEC104_FRQ_UI_RES1 = 1,
+ IEC104_FRQ_UI_RES2 = 2,
+ IEC104_FRQ_UI_RES3 = 3,
+ IEC104_FRQ_UI_RES4 = 4,
+ IEC104_FRQ_UI_RES5 = 5,
+ IEC104_FRQ_UI_RES6 = 6,
+ IEC104_FRQ_UI_RES7 = 7,
+ IEC104_FRQ_UI_RES8 = 8,
+ IEC104_FRQ_UI_RES9 = 9,
+ IEC104_FRQ_UI_RES10 = 10,
+ IEC104_FRQ_UI_RES11 = 11,
+ IEC104_FRQ_UI_RES12 = 12,
+ IEC104_FRQ_UI_RES13 = 13,
+ IEC104_FRQ_UI_RES14 = 14,
+ IEC104_FRQ_UI_RES15 = 15,
+ IEC104_FRQ_UI_RES16 = 16,
+ IEC104_FRQ_UI_RES17 = 17,
+ IEC104_FRQ_UI_RES18 = 18,
+ IEC104_FRQ_UI_RES19 = 19,
+ IEC104_FRQ_UI_RES20 = 20,
+ IEC104_FRQ_UI_RES21 = 21,
+ IEC104_FRQ_UI_RES22 = 22,
+ IEC104_FRQ_UI_RES23 = 23,
+ IEC104_FRQ_UI_RES24 = 24,
+ IEC104_FRQ_UI_RES25 = 25,
+ IEC104_FRQ_UI_RES26 = 26,
+ IEC104_FRQ_UI_RES27 = 27,
+ IEC104_FRQ_UI_RES28 = 28,
+ IEC104_FRQ_UI_RES29 = 29,
+ IEC104_FRQ_UI_RES30 = 30,
+ IEC104_FRQ_UI_RES31 = 31,
+ IEC104_FRQ_UI_RES32 = 32,
+ IEC104_FRQ_UI_RES33 = 33,
+ IEC104_FRQ_UI_RES34 = 34,
+ IEC104_FRQ_UI_RES35 = 35,
+ IEC104_FRQ_UI_RES36 = 36,
+ IEC104_FRQ_UI_RES37 = 37,
+ IEC104_FRQ_UI_RES38 = 38,
+ IEC104_FRQ_UI_RES39 = 39,
+ IEC104_FRQ_UI_RES40 = 40,
+ IEC104_FRQ_UI_RES41 = 41,
+ IEC104_FRQ_UI_RES42 = 42,
+ IEC104_FRQ_UI_RES43 = 43,
+ IEC104_FRQ_UI_RES44 = 44,
+ IEC104_FRQ_UI_RES45 = 45,
+ IEC104_FRQ_UI_RES46 = 46,
+ IEC104_FRQ_UI_RES47 = 47,
+ IEC104_FRQ_UI_RES48 = 48,
+ IEC104_FRQ_UI_RES49 = 49,
+ IEC104_FRQ_UI_RES50 = 50,
+ IEC104_FRQ_UI_RES51 = 51,
+ IEC104_FRQ_UI_RES52 = 52,
+ IEC104_FRQ_UI_RES53 = 53,
+ IEC104_FRQ_UI_RES54 = 54,
+ IEC104_FRQ_UI_RES55 = 55,
+ IEC104_FRQ_UI_RES56 = 56,
+ IEC104_FRQ_UI_RES57 = 57,
+ IEC104_FRQ_UI_RES58 = 58,
+ IEC104_FRQ_UI_RES59 = 59,
+ IEC104_FRQ_UI_RES60 = 60,
+ IEC104_FRQ_UI_RES61 = 61,
+ IEC104_FRQ_UI_RES62 = 62,
+ IEC104_FRQ_UI_RES63 = 63,
+ IEC104_FRQ_UI_RES64 = 64,
+ IEC104_FRQ_UI_RES65 = 65,
+ IEC104_FRQ_UI_RES66 = 66,
+ IEC104_FRQ_UI_RES67 = 67,
+ IEC104_FRQ_UI_RES68 = 68,
+ IEC104_FRQ_UI_RES69 = 69,
+ IEC104_FRQ_UI_RES70 = 70,
+ IEC104_FRQ_UI_RES71 = 71,
+ IEC104_FRQ_UI_RES72 = 72,
+ IEC104_FRQ_UI_RES73 = 73,
+ IEC104_FRQ_UI_RES74 = 74,
+ IEC104_FRQ_UI_RES75 = 75,
+ IEC104_FRQ_UI_RES76 = 76,
+ IEC104_FRQ_UI_RES77 = 77,
+ IEC104_FRQ_UI_RES78 = 78,
+ IEC104_FRQ_UI_RES79 = 79,
+ IEC104_FRQ_UI_RES80 = 80,
+ IEC104_FRQ_UI_RES81 = 81,
+ IEC104_FRQ_UI_RES82 = 82,
+ IEC104_FRQ_UI_RES83 = 83,
+ IEC104_FRQ_UI_RES84 = 84,
+ IEC104_FRQ_UI_RES85 = 85,
+ IEC104_FRQ_UI_RES86 = 86,
+ IEC104_FRQ_UI_RES87 = 87,
+ IEC104_FRQ_UI_RES88 = 88,
+ IEC104_FRQ_UI_RES89 = 89,
+ IEC104_FRQ_UI_RES90 = 90,
+ IEC104_FRQ_UI_RES91 = 91,
+ IEC104_FRQ_UI_RES92 = 92,
+ IEC104_FRQ_UI_RES93 = 93,
+ IEC104_FRQ_UI_RES94 = 94,
+ IEC104_FRQ_UI_RES95 = 95,
+ IEC104_FRQ_UI_RES96 = 96,
+ IEC104_FRQ_UI_RES97 = 97,
+ IEC104_FRQ_UI_RES98 = 98,
+ IEC104_FRQ_UI_RES99 = 99,
+ IEC104_FRQ_UI_RES100 = 100,
+ IEC104_FRQ_UI_RES101 = 101,
+ IEC104_FRQ_UI_RES102 = 102,
+ IEC104_FRQ_UI_RES103 = 103,
+ IEC104_FRQ_UI_RES104 = 104,
+ IEC104_FRQ_UI_RES105 = 105,
+ IEC104_FRQ_UI_RES106 = 106,
+ IEC104_FRQ_UI_RES107 = 107,
+ IEC104_FRQ_UI_RES108 = 108,
+ IEC104_FRQ_UI_RES109 = 109,
+ IEC104_FRQ_UI_RES110 = 110,
+ IEC104_FRQ_UI_RES111 = 111,
+ IEC104_FRQ_UI_RES112 = 112,
+ IEC104_FRQ_UI_RES113 = 113,
+ IEC104_FRQ_UI_RES114 = 114,
+ IEC104_FRQ_UI_RES115 = 115,
+ IEC104_FRQ_UI_RES116 = 116,
+ IEC104_FRQ_UI_RES117 = 117,
+ IEC104_FRQ_UI_RES118 = 118,
+ IEC104_FRQ_UI_RES119 = 119,
+ IEC104_FRQ_UI_RES120 = 120,
+ IEC104_FRQ_UI_RES121 = 121,
+ IEC104_FRQ_UI_RES122 = 122,
+ IEC104_FRQ_UI_RES123 = 123,
+ IEC104_FRQ_UI_RES124 = 124,
+ IEC104_FRQ_UI_RES125 = 125,
+ IEC104_FRQ_UI_RES126 = 126,
+ IEC104_FRQ_UI_RES127 = 127,
+};
+
+enum Iec104SrqUiEnum
+{
+ IEC104_SRQ_UI_DEFAULT = 0,
+ IEC104_SRQ_UI_RES1 = 1,
+ IEC104_SRQ_UI_RES2 = 2,
+ IEC104_SRQ_UI_RES3 = 3,
+ IEC104_SRQ_UI_RES4 = 4,
+ IEC104_SRQ_UI_RES5 = 5,
+ IEC104_SRQ_UI_RES6 = 6,
+ IEC104_SRQ_UI_RES7 = 7,
+ IEC104_SRQ_UI_RES8 = 8,
+ IEC104_SRQ_UI_RES9 = 9,
+ IEC104_SRQ_UI_RES10 = 10,
+ IEC104_SRQ_UI_RES11 = 11,
+ IEC104_SRQ_UI_RES12 = 12,
+ IEC104_SRQ_UI_RES13 = 13,
+ IEC104_SRQ_UI_RES14 = 14,
+ IEC104_SRQ_UI_RES15 = 15,
+ IEC104_SRQ_UI_RES16 = 16,
+ IEC104_SRQ_UI_RES17 = 17,
+ IEC104_SRQ_UI_RES18 = 18,
+ IEC104_SRQ_UI_RES19 = 19,
+ IEC104_SRQ_UI_RES20 = 20,
+ IEC104_SRQ_UI_RES21 = 21,
+ IEC104_SRQ_UI_RES22 = 22,
+ IEC104_SRQ_UI_RES23 = 23,
+ IEC104_SRQ_UI_RES24 = 24,
+ IEC104_SRQ_UI_RES25 = 25,
+ IEC104_SRQ_UI_RES26 = 26,
+ IEC104_SRQ_UI_RES27 = 27,
+ IEC104_SRQ_UI_RES28 = 28,
+ IEC104_SRQ_UI_RES29 = 29,
+ IEC104_SRQ_UI_RES30 = 30,
+ IEC104_SRQ_UI_RES31 = 31,
+ IEC104_SRQ_UI_RES32 = 32,
+ IEC104_SRQ_UI_RES33 = 33,
+ IEC104_SRQ_UI_RES34 = 34,
+ IEC104_SRQ_UI_RES35 = 35,
+ IEC104_SRQ_UI_RES36 = 36,
+ IEC104_SRQ_UI_RES37 = 37,
+ IEC104_SRQ_UI_RES38 = 38,
+ IEC104_SRQ_UI_RES39 = 39,
+ IEC104_SRQ_UI_RES40 = 40,
+ IEC104_SRQ_UI_RES41 = 41,
+ IEC104_SRQ_UI_RES42 = 42,
+ IEC104_SRQ_UI_RES43 = 43,
+ IEC104_SRQ_UI_RES44 = 44,
+ IEC104_SRQ_UI_RES45 = 45,
+ IEC104_SRQ_UI_RES46 = 46,
+ IEC104_SRQ_UI_RES47 = 47,
+ IEC104_SRQ_UI_RES48 = 48,
+ IEC104_SRQ_UI_RES49 = 49,
+ IEC104_SRQ_UI_RES50 = 50,
+ IEC104_SRQ_UI_RES51 = 51,
+ IEC104_SRQ_UI_RES52 = 52,
+ IEC104_SRQ_UI_RES53 = 53,
+ IEC104_SRQ_UI_RES54 = 54,
+ IEC104_SRQ_UI_RES55 = 55,
+ IEC104_SRQ_UI_RES56 = 56,
+ IEC104_SRQ_UI_RES57 = 57,
+ IEC104_SRQ_UI_RES58 = 58,
+ IEC104_SRQ_UI_RES59 = 59,
+ IEC104_SRQ_UI_RES60 = 60,
+ IEC104_SRQ_UI_RES61 = 61,
+ IEC104_SRQ_UI_RES62 = 62,
+ IEC104_SRQ_UI_RES63 = 63,
+ IEC104_SRQ_UI_RES64 = 64,
+ IEC104_SRQ_UI_RES65 = 65,
+ IEC104_SRQ_UI_RES66 = 66,
+ IEC104_SRQ_UI_RES67 = 67,
+ IEC104_SRQ_UI_RES68 = 68,
+ IEC104_SRQ_UI_RES69 = 69,
+ IEC104_SRQ_UI_RES70 = 70,
+ IEC104_SRQ_UI_RES71 = 71,
+ IEC104_SRQ_UI_RES72 = 72,
+ IEC104_SRQ_UI_RES73 = 73,
+ IEC104_SRQ_UI_RES74 = 74,
+ IEC104_SRQ_UI_RES75 = 75,
+ IEC104_SRQ_UI_RES76 = 76,
+ IEC104_SRQ_UI_RES77 = 77,
+ IEC104_SRQ_UI_RES78 = 78,
+ IEC104_SRQ_UI_RES79 = 79,
+ IEC104_SRQ_UI_RES80 = 80,
+ IEC104_SRQ_UI_RES81 = 81,
+ IEC104_SRQ_UI_RES82 = 82,
+ IEC104_SRQ_UI_RES83 = 83,
+ IEC104_SRQ_UI_RES84 = 84,
+ IEC104_SRQ_UI_RES85 = 85,
+ IEC104_SRQ_UI_RES86 = 86,
+ IEC104_SRQ_UI_RES87 = 87,
+ IEC104_SRQ_UI_RES88 = 88,
+ IEC104_SRQ_UI_RES89 = 89,
+ IEC104_SRQ_UI_RES90 = 90,
+ IEC104_SRQ_UI_RES91 = 91,
+ IEC104_SRQ_UI_RES92 = 92,
+ IEC104_SRQ_UI_RES93 = 93,
+ IEC104_SRQ_UI_RES94 = 94,
+ IEC104_SRQ_UI_RES95 = 95,
+ IEC104_SRQ_UI_RES96 = 96,
+ IEC104_SRQ_UI_RES97 = 97,
+ IEC104_SRQ_UI_RES98 = 98,
+ IEC104_SRQ_UI_RES99 = 99,
+ IEC104_SRQ_UI_RES100 = 100,
+ IEC104_SRQ_UI_RES101 = 101,
+ IEC104_SRQ_UI_RES102 = 102,
+ IEC104_SRQ_UI_RES103 = 103,
+ IEC104_SRQ_UI_RES104 = 104,
+ IEC104_SRQ_UI_RES105 = 105,
+ IEC104_SRQ_UI_RES106 = 106,
+ IEC104_SRQ_UI_RES107 = 107,
+ IEC104_SRQ_UI_RES108 = 108,
+ IEC104_SRQ_UI_RES109 = 109,
+ IEC104_SRQ_UI_RES110 = 110,
+ IEC104_SRQ_UI_RES111 = 111,
+ IEC104_SRQ_UI_RES112 = 112,
+ IEC104_SRQ_UI_RES113 = 113,
+ IEC104_SRQ_UI_RES114 = 114,
+ IEC104_SRQ_UI_RES115 = 115,
+ IEC104_SRQ_UI_RES116 = 116,
+ IEC104_SRQ_UI_RES117 = 117,
+ IEC104_SRQ_UI_RES118 = 118,
+ IEC104_SRQ_UI_RES119 = 119,
+ IEC104_SRQ_UI_RES120 = 120,
+ IEC104_SRQ_UI_RES121 = 121,
+ IEC104_SRQ_UI_RES122 = 122,
+ IEC104_SRQ_UI_RES123 = 123,
+ IEC104_SRQ_UI_RES124 = 124,
+ IEC104_SRQ_UI_RES125 = 125,
+ IEC104_SRQ_UI_RES126 = 126,
+ IEC104_SRQ_UI_RES127 = 127,
+};
+
+enum Iec104ScqUi1Enum
+{
+ IEC104_SCQ_UI1_DEFAULT = 0,
+ IEC104_SCQ_UI1_SELECTFILE = 1,
+ IEC104_SCQ_UI1_REQUESTFILE = 2,
+ IEC104_SCQ_UI1_DEACTFILE = 3,
+ IEC104_SCQ_UI1_DELETEFILE = 4,
+ IEC104_SCQ_UI1_SELECTSECTION = 5,
+ IEC104_SCQ_UI1_REQUESTSECTION = 6,
+ IEC104_SCQ_UI1_DEACTSECTION = 7,
+ IEC104_SCQ_UI1_RES8 = 8,
+ IEC104_SCQ_UI1_RES9 = 9,
+ IEC104_SCQ_UI1_RES10 = 10,
+ IEC104_SCQ_UI1_RES11 = 11,
+ IEC104_SCQ_UI1_RES12 = 12,
+ IEC104_SCQ_UI1_RES13 = 13,
+ IEC104_SCQ_UI1_RES14 = 14,
+ IEC104_SCQ_UI1_RES15 = 15,
+};
+
+enum Iec104ScqUi2Enum
+{
+ IEC104_SCQ_UI2_DEFAULT = 0,
+ IEC104_SCQ_UI2_MEMUNAVAIL = 1,
+ IEC104_SCQ_UI2_CHKSMFAILED = 2,
+ IEC104_SCQ_UI2_UNEXPECTEDCOMM = 3,
+ IEC104_SCQ_UI2_UNEXPECTEDNOF = 4,
+ IEC104_SCQ_UI2_UNEXPECTEDNOS = 5,
+ IEC104_SCQ_UI2_RES6 = 6,
+ IEC104_SCQ_UI2_RES7 = 7,
+ IEC104_SCQ_UI2_RES8 = 8,
+ IEC104_SCQ_UI2_RES9 = 9,
+ IEC104_SCQ_UI2_RES10 = 10,
+ IEC104_SCQ_UI2_RES11 = 11,
+ IEC104_SCQ_UI2_RES12 = 12,
+ IEC104_SCQ_UI2_RES13 = 13,
+ IEC104_SCQ_UI2_RES14 = 14,
+ IEC104_SCQ_UI2_RES15 = 15,
+};
+
+enum Iec104LsqEnum
+{
+ IEC104_LSQ_NOTUSED = 0,
+ IEC104_LSQ_FILETRANSFERWITHOUTDEACT = 1,
+ IEC104_LSQ_FILETRANSFERWITHDEACT = 2,
+ IEC104_LSQ_SECTIONTRANSFERWITHOUTDEACT = 3,
+ IEC104_LSQ_SECTIONTRANSFERWITHDEACT = 4,
+ IEC104_LSQ_RES5 = 5,
+ IEC104_LSQ_RES6 = 6,
+ IEC104_LSQ_RES7 = 7,
+ IEC104_LSQ_RES8 = 8,
+ IEC104_LSQ_RES9 = 9,
+ IEC104_LSQ_RES10 = 10,
+ IEC104_LSQ_RES11 = 11,
+ IEC104_LSQ_RES12 = 12,
+ IEC104_LSQ_RES13 = 13,
+ IEC104_LSQ_RES14 = 14,
+ IEC104_LSQ_RES15 = 15,
+ IEC104_LSQ_RES16 = 16,
+ IEC104_LSQ_RES17 = 17,
+ IEC104_LSQ_RES18 = 18,
+ IEC104_LSQ_RES19 = 19,
+ IEC104_LSQ_RES20 = 20,
+ IEC104_LSQ_RES21 = 21,
+ IEC104_LSQ_RES22 = 22,
+ IEC104_LSQ_RES23 = 23,
+ IEC104_LSQ_RES24 = 24,
+ IEC104_LSQ_RES25 = 25,
+ IEC104_LSQ_RES26 = 26,
+ IEC104_LSQ_RES27 = 27,
+ IEC104_LSQ_RES28 = 28,
+ IEC104_LSQ_RES29 = 29,
+ IEC104_LSQ_RES30 = 30,
+ IEC104_LSQ_RES31 = 31,
+ IEC104_LSQ_RES32 = 32,
+ IEC104_LSQ_RES33 = 33,
+ IEC104_LSQ_RES34 = 34,
+ IEC104_LSQ_RES35 = 35,
+ IEC104_LSQ_RES36 = 36,
+ IEC104_LSQ_RES37 = 37,
+ IEC104_LSQ_RES38 = 38,
+ IEC104_LSQ_RES39 = 39,
+ IEC104_LSQ_RES40 = 40,
+ IEC104_LSQ_RES41 = 41,
+ IEC104_LSQ_RES42 = 42,
+ IEC104_LSQ_RES43 = 43,
+ IEC104_LSQ_RES44 = 44,
+ IEC104_LSQ_RES45 = 45,
+ IEC104_LSQ_RES46 = 46,
+ IEC104_LSQ_RES47 = 47,
+ IEC104_LSQ_RES48 = 48,
+ IEC104_LSQ_RES49 = 49,
+ IEC104_LSQ_RES50 = 50,
+ IEC104_LSQ_RES51 = 51,
+ IEC104_LSQ_RES52 = 52,
+ IEC104_LSQ_RES53 = 53,
+ IEC104_LSQ_RES54 = 54,
+ IEC104_LSQ_RES55 = 55,
+ IEC104_LSQ_RES56 = 56,
+ IEC104_LSQ_RES57 = 57,
+ IEC104_LSQ_RES58 = 58,
+ IEC104_LSQ_RES59 = 59,
+ IEC104_LSQ_RES60 = 60,
+ IEC104_LSQ_RES61 = 61,
+ IEC104_LSQ_RES62 = 62,
+ IEC104_LSQ_RES63 = 63,
+ IEC104_LSQ_RES64 = 64,
+ IEC104_LSQ_RES65 = 65,
+ IEC104_LSQ_RES66 = 66,
+ IEC104_LSQ_RES67 = 67,
+ IEC104_LSQ_RES68 = 68,
+ IEC104_LSQ_RES69 = 69,
+ IEC104_LSQ_RES70 = 70,
+ IEC104_LSQ_RES71 = 71,
+ IEC104_LSQ_RES72 = 72,
+ IEC104_LSQ_RES73 = 73,
+ IEC104_LSQ_RES74 = 74,
+ IEC104_LSQ_RES75 = 75,
+ IEC104_LSQ_RES76 = 76,
+ IEC104_LSQ_RES77 = 77,
+ IEC104_LSQ_RES78 = 78,
+ IEC104_LSQ_RES79 = 79,
+ IEC104_LSQ_RES80 = 80,
+ IEC104_LSQ_RES81 = 81,
+ IEC104_LSQ_RES82 = 82,
+ IEC104_LSQ_RES83 = 83,
+ IEC104_LSQ_RES84 = 84,
+ IEC104_LSQ_RES85 = 85,
+ IEC104_LSQ_RES86 = 86,
+ IEC104_LSQ_RES87 = 87,
+ IEC104_LSQ_RES88 = 88,
+ IEC104_LSQ_RES89 = 89,
+ IEC104_LSQ_RES90 = 90,
+ IEC104_LSQ_RES91 = 91,
+ IEC104_LSQ_RES92 = 92,
+ IEC104_LSQ_RES93 = 93,
+ IEC104_LSQ_RES94 = 94,
+ IEC104_LSQ_RES95 = 95,
+ IEC104_LSQ_RES96 = 96,
+ IEC104_LSQ_RES97 = 97,
+ IEC104_LSQ_RES98 = 98,
+ IEC104_LSQ_RES99 = 99,
+ IEC104_LSQ_RES100 = 100,
+ IEC104_LSQ_RES101 = 101,
+ IEC104_LSQ_RES102 = 102,
+ IEC104_LSQ_RES103 = 103,
+ IEC104_LSQ_RES104 = 104,
+ IEC104_LSQ_RES105 = 105,
+ IEC104_LSQ_RES106 = 106,
+ IEC104_LSQ_RES107 = 107,
+ IEC104_LSQ_RES108 = 108,
+ IEC104_LSQ_RES109 = 109,
+ IEC104_LSQ_RES110 = 110,
+ IEC104_LSQ_RES111 = 111,
+ IEC104_LSQ_RES112 = 112,
+ IEC104_LSQ_RES113 = 113,
+ IEC104_LSQ_RES114 = 114,
+ IEC104_LSQ_RES115 = 115,
+ IEC104_LSQ_RES116 = 116,
+ IEC104_LSQ_RES117 = 117,
+ IEC104_LSQ_RES118 = 118,
+ IEC104_LSQ_RES119 = 119,
+ IEC104_LSQ_RES120 = 120,
+ IEC104_LSQ_RES121 = 121,
+ IEC104_LSQ_RES122 = 122,
+ IEC104_LSQ_RES123 = 123,
+ IEC104_LSQ_RES124 = 124,
+ IEC104_LSQ_RES125 = 125,
+ IEC104_LSQ_RES126 = 126,
+ IEC104_LSQ_RES127 = 127,
+ IEC104_LSQ_RES128 = 128,
+ IEC104_LSQ_RES129 = 129,
+ IEC104_LSQ_RES130 = 130,
+ IEC104_LSQ_RES131 = 131,
+ IEC104_LSQ_RES132 = 132,
+ IEC104_LSQ_RES133 = 133,
+ IEC104_LSQ_RES134 = 134,
+ IEC104_LSQ_RES135 = 135,
+ IEC104_LSQ_RES136 = 136,
+ IEC104_LSQ_RES137 = 137,
+ IEC104_LSQ_RES138 = 138,
+ IEC104_LSQ_RES139 = 139,
+ IEC104_LSQ_RES140 = 140,
+ IEC104_LSQ_RES141 = 141,
+ IEC104_LSQ_RES142 = 142,
+ IEC104_LSQ_RES143 = 143,
+ IEC104_LSQ_RES144 = 144,
+ IEC104_LSQ_RES145 = 145,
+ IEC104_LSQ_RES146 = 146,
+ IEC104_LSQ_RES147 = 147,
+ IEC104_LSQ_RES148 = 148,
+ IEC104_LSQ_RES149 = 149,
+ IEC104_LSQ_RES150 = 150,
+ IEC104_LSQ_RES151 = 151,
+ IEC104_LSQ_RES152 = 152,
+ IEC104_LSQ_RES153 = 153,
+ IEC104_LSQ_RES154 = 154,
+ IEC104_LSQ_RES155 = 155,
+ IEC104_LSQ_RES156 = 156,
+ IEC104_LSQ_RES157 = 157,
+ IEC104_LSQ_RES158 = 158,
+ IEC104_LSQ_RES159 = 159,
+ IEC104_LSQ_RES160 = 160,
+ IEC104_LSQ_RES161 = 161,
+ IEC104_LSQ_RES162 = 162,
+ IEC104_LSQ_RES163 = 163,
+ IEC104_LSQ_RES164 = 164,
+ IEC104_LSQ_RES165 = 165,
+ IEC104_LSQ_RES166 = 166,
+ IEC104_LSQ_RES167 = 167,
+ IEC104_LSQ_RES168 = 168,
+ IEC104_LSQ_RES169 = 169,
+ IEC104_LSQ_RES170 = 170,
+ IEC104_LSQ_RES171 = 171,
+ IEC104_LSQ_RES172 = 172,
+ IEC104_LSQ_RES173 = 173,
+ IEC104_LSQ_RES174 = 174,
+ IEC104_LSQ_RES175 = 175,
+ IEC104_LSQ_RES176 = 176,
+ IEC104_LSQ_RES177 = 177,
+ IEC104_LSQ_RES178 = 178,
+ IEC104_LSQ_RES179 = 179,
+ IEC104_LSQ_RES180 = 180,
+ IEC104_LSQ_RES181 = 181,
+ IEC104_LSQ_RES182 = 182,
+ IEC104_LSQ_RES183 = 183,
+ IEC104_LSQ_RES184 = 184,
+ IEC104_LSQ_RES185 = 185,
+ IEC104_LSQ_RES186 = 186,
+ IEC104_LSQ_RES187 = 187,
+ IEC104_LSQ_RES188 = 188,
+ IEC104_LSQ_RES189 = 189,
+ IEC104_LSQ_RES190 = 190,
+ IEC104_LSQ_RES191 = 191,
+ IEC104_LSQ_RES192 = 192,
+ IEC104_LSQ_RES193 = 193,
+ IEC104_LSQ_RES194 = 194,
+ IEC104_LSQ_RES195 = 195,
+ IEC104_LSQ_RES196 = 196,
+ IEC104_LSQ_RES197 = 197,
+ IEC104_LSQ_RES198 = 198,
+ IEC104_LSQ_RES199 = 199,
+ IEC104_LSQ_RES200 = 200,
+ IEC104_LSQ_RES201 = 201,
+ IEC104_LSQ_RES202 = 202,
+ IEC104_LSQ_RES203 = 203,
+ IEC104_LSQ_RES204 = 204,
+ IEC104_LSQ_RES205 = 205,
+ IEC104_LSQ_RES206 = 206,
+ IEC104_LSQ_RES207 = 207,
+ IEC104_LSQ_RES208 = 208,
+ IEC104_LSQ_RES209 = 209,
+ IEC104_LSQ_RES210 = 210,
+ IEC104_LSQ_RES211 = 211,
+ IEC104_LSQ_RES212 = 212,
+ IEC104_LSQ_RES213 = 213,
+ IEC104_LSQ_RES214 = 214,
+ IEC104_LSQ_RES215 = 215,
+ IEC104_LSQ_RES216 = 216,
+ IEC104_LSQ_RES217 = 217,
+ IEC104_LSQ_RES218 = 218,
+ IEC104_LSQ_RES219 = 219,
+ IEC104_LSQ_RES220 = 220,
+ IEC104_LSQ_RES221 = 221,
+ IEC104_LSQ_RES222 = 222,
+ IEC104_LSQ_RES223 = 223,
+ IEC104_LSQ_RES224 = 224,
+ IEC104_LSQ_RES225 = 225,
+ IEC104_LSQ_RES226 = 226,
+ IEC104_LSQ_RES227 = 227,
+ IEC104_LSQ_RES228 = 228,
+ IEC104_LSQ_RES229 = 229,
+ IEC104_LSQ_RES230 = 230,
+ IEC104_LSQ_RES231 = 231,
+ IEC104_LSQ_RES232 = 232,
+ IEC104_LSQ_RES233 = 233,
+ IEC104_LSQ_RES234 = 234,
+ IEC104_LSQ_RES235 = 235,
+ IEC104_LSQ_RES236 = 236,
+ IEC104_LSQ_RES237 = 237,
+ IEC104_LSQ_RES238 = 238,
+ IEC104_LSQ_RES239 = 239,
+ IEC104_LSQ_RES240 = 240,
+ IEC104_LSQ_RES241 = 241,
+ IEC104_LSQ_RES242 = 242,
+ IEC104_LSQ_RES243 = 243,
+ IEC104_LSQ_RES244 = 244,
+ IEC104_LSQ_RES245 = 245,
+ IEC104_LSQ_RES246 = 246,
+ IEC104_LSQ_RES247 = 247,
+ IEC104_LSQ_RES248 = 248,
+ IEC104_LSQ_RES249 = 249,
+ IEC104_LSQ_RES250 = 250,
+ IEC104_LSQ_RES251 = 251,
+ IEC104_LSQ_RES252 = 252,
+ IEC104_LSQ_RES253 = 253,
+ IEC104_LSQ_RES254 = 254,
+ IEC104_LSQ_RES255 = 255,
+};
+
+enum Iec104AfqUi1Enum
+{
+ IEC104_AFQ_UI1_NOTUSED = 0,
+ IEC104_AFQ_UI1_POSFILEACK = 1,
+ IEC104_AFQ_UI1_NEGFILEACK = 2,
+ IEC104_AFQ_UI1_POSSECTIONACK = 3,
+ IEC104_AFQ_UI1_NEGSECTIONACK = 4,
+ IEC104_AFQ_UI1_RES5 = 5,
+ IEC104_AFQ_UI1_RES6 = 6,
+ IEC104_AFQ_UI1_RES7 = 7,
+ IEC104_AFQ_UI1_RES8 = 8,
+ IEC104_AFQ_UI1_RES9 = 9,
+ IEC104_AFQ_UI1_RES10 = 10,
+ IEC104_AFQ_UI1_RES11 = 11,
+ IEC104_AFQ_UI1_RES12 = 12,
+ IEC104_AFQ_UI1_RES13 = 13,
+ IEC104_AFQ_UI1_RES14 = 14,
+ IEC104_AFQ_UI1_RES15 = 15,
+};
+
+enum Iec104AfqUi2Enum
+{
+ IEC104_AFQ_UI2_DEFAULT = 0,
+ IEC104_AFQ_UI2_MEMUNAVAIL = 1,
+ IEC104_AFQ_UI2_CHKSMFAILED = 2,
+ IEC104_AFQ_UI2_UNEXPECTEDCOMM = 3,
+ IEC104_AFQ_UI2_UNEXPECTEDNOF = 4,
+ IEC104_AFQ_UI2_UNEXPECTEDNOS = 5,
+ IEC104_AFQ_UI2_RES6 = 6,
+ IEC104_AFQ_UI2_RES7 = 7,
+ IEC104_AFQ_UI2_RES8 = 8,
+ IEC104_AFQ_UI2_RES9 = 9,
+ IEC104_AFQ_UI2_RES10 = 10,
+ IEC104_AFQ_UI2_RES11 = 11,
+ IEC104_AFQ_UI2_RES12 = 12,
+ IEC104_AFQ_UI2_RES13 = 13,
+ IEC104_AFQ_UI2_RES14 = 14,
+ IEC104_AFQ_UI2_RES15 = 15,
+};
+
+enum Iec104DiqDpiEnum
+{
+ IEC104_DIQ_DPI_INDETERMSTATE1 = 0,
+ IEC104_DIQ_DPI_STATEOFF = 1,
+ IEC104_DIQ_DPI_STATEON = 2,
+ IEC104_DIQ_DPI_INDETERMSTATE2 = 3,
+};
+
+enum Iec104SepEsEnum
+{
+ IEC104_SEP_ES_INDETERMSTATE1 = 0,
+ IEC104_SEP_ES_STATEOFF = 1,
+ IEC104_SEP_ES_STATEON = 2,
+ IEC104_SEP_ES_INDETERMSTATE2 = 3,
+};
+
+enum Iec104DcoDcsEnum
+{
+ IEC104_DCO_DCS_NOTPERMITTED1 = 0,
+ IEC104_DCO_DCS_STATEOFF = 1,
+ IEC104_DCO_DCS_STATEON = 2,
+ IEC104_DCO_DCS_NOTPERMITTED2 = 3,
+};
+
+enum Iec104RcoRcsEnum
+{
+ IEC104_RCO_RCS_NOTPERMITTED1 = 0,
+ IEC104_RCO_RCS_STEPLOWER = 1,
+ IEC104_RCO_RCS_STEPHIGHER = 2,
+ IEC104_RCO_RCS_NOTPERMITTED2 = 3,
+};
+
+enum Iec104SofStatusEnum
+{
+ IEC104_SOF_STATUS_DEFAULT = 0,
+ IEC104_SOF_STATUS_RES1 = 1,
+ IEC104_SOF_STATUS_RES2 = 2,
+ IEC104_SOF_STATUS_RES3 = 3,
+ IEC104_SOF_STATUS_RES4 = 4,
+ IEC104_SOF_STATUS_RES5 = 5,
+ IEC104_SOF_STATUS_RES6 = 6,
+ IEC104_SOF_STATUS_RES7 = 7,
+ IEC104_SOF_STATUS_RES8 = 8,
+ IEC104_SOF_STATUS_RES9 = 9,
+ IEC104_SOF_STATUS_RES10 = 10,
+ IEC104_SOF_STATUS_RES11 = 11,
+ IEC104_SOF_STATUS_RES12 = 12,
+ IEC104_SOF_STATUS_RES13 = 13,
+ IEC104_SOF_STATUS_RES14 = 14,
+ IEC104_SOF_STATUS_RES15 = 15,
+ IEC104_SOF_STATUS_RES16 = 16,
+ IEC104_SOF_STATUS_RES17 = 17,
+ IEC104_SOF_STATUS_RES18 = 18,
+ IEC104_SOF_STATUS_RES19 = 19,
+ IEC104_SOF_STATUS_RES20 = 20,
+ IEC104_SOF_STATUS_RES21 = 21,
+ IEC104_SOF_STATUS_RES22 = 22,
+ IEC104_SOF_STATUS_RES23 = 23,
+ IEC104_SOF_STATUS_RES24 = 24,
+ IEC104_SOF_STATUS_RES25 = 25,
+ IEC104_SOF_STATUS_RES26 = 26,
+ IEC104_SOF_STATUS_RES27 = 27,
+ IEC104_SOF_STATUS_RES28 = 28,
+ IEC104_SOF_STATUS_RES29 = 29,
+ IEC104_SOF_STATUS_RES30 = 30,
+ IEC104_SOF_STATUS_RES31 = 31,
+};
+
+enum Iec104QosQlEnum
+{
+ IEC104_QOS_QL_DEFAULT = 0,
+ IEC104_QOS_QL_RES1 = 1,
+ IEC104_QOS_QL_RES2 = 2,
+ IEC104_QOS_QL_RES3 = 3,
+ IEC104_QOS_QL_RES4 = 4,
+ IEC104_QOS_QL_RES5 = 5,
+ IEC104_QOS_QL_RES6 = 6,
+ IEC104_QOS_QL_RES7 = 7,
+ IEC104_QOS_QL_RES8 = 8,
+ IEC104_QOS_QL_RES9 = 9,
+ IEC104_QOS_QL_RES10 = 10,
+ IEC104_QOS_QL_RES11 = 11,
+ IEC104_QOS_QL_RES12 = 12,
+ IEC104_QOS_QL_RES13 = 13,
+ IEC104_QOS_QL_RES14 = 14,
+ IEC104_QOS_QL_RES15 = 15,
+ IEC104_QOS_QL_RES16 = 16,
+ IEC104_QOS_QL_RES17 = 17,
+ IEC104_QOS_QL_RES18 = 18,
+ IEC104_QOS_QL_RES19 = 19,
+ IEC104_QOS_QL_RES20 = 20,
+ IEC104_QOS_QL_RES21 = 21,
+ IEC104_QOS_QL_RES22 = 22,
+ IEC104_QOS_QL_RES23 = 23,
+ IEC104_QOS_QL_RES24 = 24,
+ IEC104_QOS_QL_RES25 = 25,
+ IEC104_QOS_QL_RES26 = 26,
+ IEC104_QOS_QL_RES27 = 27,
+ IEC104_QOS_QL_RES28 = 28,
+ IEC104_QOS_QL_RES29 = 29,
+ IEC104_QOS_QL_RES30 = 30,
+ IEC104_QOS_QL_RES31 = 31,
+ IEC104_QOS_QL_RES32 = 32,
+ IEC104_QOS_QL_RES33 = 33,
+ IEC104_QOS_QL_RES34 = 34,
+ IEC104_QOS_QL_RES35 = 35,
+ IEC104_QOS_QL_RES36 = 36,
+ IEC104_QOS_QL_RES37 = 37,
+ IEC104_QOS_QL_RES38 = 38,
+ IEC104_QOS_QL_RES39 = 39,
+ IEC104_QOS_QL_RES40 = 40,
+ IEC104_QOS_QL_RES41 = 41,
+ IEC104_QOS_QL_RES42 = 42,
+ IEC104_QOS_QL_RES43 = 43,
+ IEC104_QOS_QL_RES44 = 44,
+ IEC104_QOS_QL_RES45 = 45,
+ IEC104_QOS_QL_RES46 = 46,
+ IEC104_QOS_QL_RES47 = 47,
+ IEC104_QOS_QL_RES48 = 48,
+ IEC104_QOS_QL_RES49 = 49,
+ IEC104_QOS_QL_RES50 = 50,
+ IEC104_QOS_QL_RES51 = 51,
+ IEC104_QOS_QL_RES52 = 52,
+ IEC104_QOS_QL_RES53 = 53,
+ IEC104_QOS_QL_RES54 = 54,
+ IEC104_QOS_QL_RES55 = 55,
+ IEC104_QOS_QL_RES56 = 56,
+ IEC104_QOS_QL_RES57 = 57,
+ IEC104_QOS_QL_RES58 = 58,
+ IEC104_QOS_QL_RES59 = 59,
+ IEC104_QOS_QL_RES60 = 60,
+ IEC104_QOS_QL_RES61 = 61,
+ IEC104_QOS_QL_RES62 = 62,
+ IEC104_QOS_QL_RES63 = 63,
+ IEC104_QOS_QL_RES64 = 64,
+ IEC104_QOS_QL_RES65 = 65,
+ IEC104_QOS_QL_RES66 = 66,
+ IEC104_QOS_QL_RES67 = 67,
+ IEC104_QOS_QL_RES68 = 68,
+ IEC104_QOS_QL_RES69 = 69,
+ IEC104_QOS_QL_RES70 = 70,
+ IEC104_QOS_QL_RES71 = 71,
+ IEC104_QOS_QL_RES72 = 72,
+ IEC104_QOS_QL_RES73 = 73,
+ IEC104_QOS_QL_RES74 = 74,
+ IEC104_QOS_QL_RES75 = 75,
+ IEC104_QOS_QL_RES76 = 76,
+ IEC104_QOS_QL_RES77 = 77,
+ IEC104_QOS_QL_RES78 = 78,
+ IEC104_QOS_QL_RES79 = 79,
+ IEC104_QOS_QL_RES80 = 80,
+ IEC104_QOS_QL_RES81 = 81,
+ IEC104_QOS_QL_RES82 = 82,
+ IEC104_QOS_QL_RES83 = 83,
+ IEC104_QOS_QL_RES84 = 84,
+ IEC104_QOS_QL_RES85 = 85,
+ IEC104_QOS_QL_RES86 = 86,
+ IEC104_QOS_QL_RES87 = 87,
+ IEC104_QOS_QL_RES88 = 88,
+ IEC104_QOS_QL_RES89 = 89,
+ IEC104_QOS_QL_RES90 = 90,
+ IEC104_QOS_QL_RES91 = 91,
+ IEC104_QOS_QL_RES92 = 92,
+ IEC104_QOS_QL_RES93 = 93,
+ IEC104_QOS_QL_RES94 = 94,
+ IEC104_QOS_QL_RES95 = 95,
+ IEC104_QOS_QL_RES96 = 96,
+ IEC104_QOS_QL_RES97 = 97,
+ IEC104_QOS_QL_RES98 = 98,
+ IEC104_QOS_QL_RES99 = 99,
+ IEC104_QOS_QL_RES100 = 100,
+ IEC104_QOS_QL_RES101 = 101,
+ IEC104_QOS_QL_RES102 = 102,
+ IEC104_QOS_QL_RES103 = 103,
+ IEC104_QOS_QL_RES104 = 104,
+ IEC104_QOS_QL_RES105 = 105,
+ IEC104_QOS_QL_RES106 = 106,
+ IEC104_QOS_QL_RES107 = 107,
+ IEC104_QOS_QL_RES108 = 108,
+ IEC104_QOS_QL_RES109 = 109,
+ IEC104_QOS_QL_RES110 = 110,
+ IEC104_QOS_QL_RES111 = 111,
+ IEC104_QOS_QL_RES112 = 112,
+ IEC104_QOS_QL_RES113 = 113,
+ IEC104_QOS_QL_RES114 = 114,
+ IEC104_QOS_QL_RES115 = 115,
+ IEC104_QOS_QL_RES116 = 116,
+ IEC104_QOS_QL_RES117 = 117,
+ IEC104_QOS_QL_RES118 = 118,
+ IEC104_QOS_QL_RES119 = 119,
+ IEC104_QOS_QL_RES120 = 120,
+ IEC104_QOS_QL_RES121 = 121,
+ IEC104_QOS_QL_RES122 = 122,
+ IEC104_QOS_QL_RES123 = 123,
+ IEC104_QOS_QL_RES124 = 124,
+ IEC104_QOS_QL_RES125 = 125,
+ IEC104_QOS_QL_RES126 = 126,
+ IEC104_QOS_QL_RES127 = 127,
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// iec104_trace.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after detect_trace.cc (author Maya Dagon <mdagon@cisco.com>)
+
+#ifndef IEC104_TRACE_H
+#define IEC104_TRACE_H
+
+// Detection trace utility
+
+#include "framework/cursor.h"
+#include "main/snort_types.h"
+#include "main/thread.h"
+
+namespace snort
+{
+struct Packet;
+class Trace;
+}
+
+extern THREAD_LOCAL const snort::Trace* iec104_trace;
+
+enum
+{
+ TRACE_IEC104_IDENTIFICATION = 0,
+};
+
+#ifdef DEBUG_MSGS
+#define print_debug_information(p, msg) debug_log(iec104_trace, TRACE_IEC104_IDENTIFICATION, p, msg);
+#else
+#define print_debug_information(...)
+#endif
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// ips_iec104_apci_type.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after ips_modbus_func.cc (author Russ Combs <rucombs@cisco.com>)
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "framework/ips_option.h"
+#include "framework/module.h"
+#include "hash/hash_key_operations.h"
+#include "protocols/packet.h"
+#include "profiler/profiler.h"
+
+#include "iec104.h"
+#include "iec104_parse_apdu.h"
+
+using namespace snort;
+
+static const char* s_name = "iec104_apci_type";
+
+//-------------------------------------------------------------------------
+// apci type lookup
+//-------------------------------------------------------------------------
+
+struct Iec104ApciTypeMap
+{
+ const char* name;
+ uint8_t apci_type;
+};
+
+/* Mapping of name -> apci type for 'iec104_apci_type' option. */
+static Iec104ApciTypeMap iec104_apci_type_map[] =
+{
+ { "u", IEC104_APCI_TYPE_U }, // unnumbered control function
+ { "U", IEC104_APCI_TYPE_U }, // unnumbered control function
+ { "unnumbered_control_function", IEC104_APCI_TYPE_U }, // unnumbered control function
+ { "s", IEC104_APCI_TYPE_S }, // numbered supervisory function
+ { "S", IEC104_APCI_TYPE_S }, // numbered supervisory function
+ { "numbered_supervisory_function", IEC104_APCI_TYPE_S }, // numbered supervisory function
+ { "i", IEC104_APCI_TYPE_I }, // information transfer format
+ { "I", IEC104_APCI_TYPE_I }, // information transfer format
+ { "information_transfer_format", IEC104_APCI_TYPE_I }, // information transfer format
+};
+
+static bool get_apci_type(const char* s, long &n)
+{
+ constexpr size_t max = (sizeof(iec104_apci_type_map) / sizeof(Iec104ApciTypeMap));
+
+ for (size_t i = 0; i < max; ++i)
+ {
+ if (!strcmp(s, iec104_apci_type_map[i].name))
+ {
+ n = iec104_apci_type_map[i].apci_type;
+ return true;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------
+// apci_type option
+//-------------------------------------------------------------------------
+
+static THREAD_LOCAL ProfileStats iec104_apci_type_prof;
+
+class Iec104ApciTypeOption: public IpsOption
+{
+public:
+ Iec104ApciTypeOption(uint16_t v) :
+ IpsOption(s_name)
+ {
+ apci_type = v;
+ }
+
+ uint32_t hash() const override;
+ bool operator==(const IpsOption&) const override;
+
+ EvalStatus eval(Cursor&, Packet*) override;
+
+public:
+ uint8_t apci_type;
+};
+
+uint32_t Iec104ApciTypeOption::hash() const
+{
+ uint32_t a = apci_type, b = IpsOption::hash(), c = 0;
+
+ mix(a, b, c);
+ finalize(a, b, c);
+
+ return c;
+}
+
+bool Iec104ApciTypeOption::operator==(const IpsOption &ips) const
+{
+ if (!IpsOption::operator==(ips))
+ {
+ return false;
+ }
+
+ const Iec104ApciTypeOption &rhs = (const Iec104ApciTypeOption&) ips;
+ return (apci_type == rhs.apci_type);
+}
+
+IpsOption::EvalStatus Iec104ApciTypeOption::eval(Cursor&, Packet* p)
+{
+ RuleProfile profile(iec104_apci_type_prof);
+
+ if (!p->flow)
+ {
+ return NO_MATCH;
+ }
+
+ if (!p->is_full_pdu())
+ {
+ return NO_MATCH;
+ }
+
+ // check if the packet apci_type matches the rule option apci_type
+ Iec104FlowData* iec104fd = (Iec104FlowData*) p->flow->get_flow_data(Iec104FlowData::inspector_id);
+ if (iec104fd and apci_type == iec104fd->ssn_data.iec104_apci_type)
+ {
+ return MATCH;
+ }
+
+ return NO_MATCH;
+}
+
+//-------------------------------------------------------------------------
+// module
+//-------------------------------------------------------------------------
+
+static const Parameter s_params[] =
+{
+ { "~", Parameter::PT_STRING, nullptr, nullptr,
+ "APCI type to match" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+#define s_help \
+ "rule option to check iec104 apci type"
+
+class Iec104ApciTypeModule: public Module
+{
+public:
+ Iec104ApciTypeModule() :
+ Module(s_name, s_help, s_params)
+ {
+ }
+
+ bool set(const char*, Value&, SnortConfig*) override;
+
+ ProfileStats* get_profile() const override
+ {
+ return &iec104_apci_type_prof;
+ }
+
+ Usage get_usage() const override
+ {
+ return DETECT;
+ }
+
+public:
+ uint8_t apci_type = IEC104_NO_APCI;
+};
+
+bool Iec104ApciTypeModule::set(const char*, Value &v, SnortConfig*)
+{
+ if (!v.is("~"))
+ {
+ return false;
+ }
+
+ long n;
+
+ if (v.strtol(n))
+ {
+ apci_type = static_cast<uint8_t>(n);
+ }
+
+ else if (get_apci_type(v.get_string(), n))
+ {
+ apci_type = static_cast<uint8_t>(n);
+ }
+
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------
+// api
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{
+ return new Iec104ApciTypeModule;
+}
+
+static void mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static IpsOption* opt_ctor(Module* m, OptTreeNode*)
+{
+ Iec104ApciTypeModule* mod = (Iec104ApciTypeModule*) m;
+ return new Iec104ApciTypeOption(mod->apci_type);
+}
+
+static void opt_dtor(IpsOption* p)
+{
+ delete p;
+}
+
+static const IpsApi ips_api =
+{
+ {
+ PT_IPS_OPTION,
+ sizeof(IpsApi),
+ IPSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ s_name,
+ s_help,
+ mod_ctor,
+ mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0,
+ PROTO_BIT__TCP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ opt_ctor,
+ opt_dtor,
+ nullptr
+};
+
+const BaseApi* ips_iec104_apci_type = &ips_api.base;
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2021-2021 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// ips_iec104_asdu_func.cc author Jared Rittle <jared.rittle@cisco.com>
+// modeled after ips_modbus_func.cc (author Russ Combs <rucombs@cisco.com>)
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "framework/ips_option.h"
+#include "framework/module.h"
+#include "hash/hash_key_operations.h"
+#include "protocols/packet.h"
+#include "profiler/profiler.h"
+
+#include "iec104.h"
+#include "iec104_parse_apdu.h"
+
+using namespace snort;
+
+static const char* s_name = "iec104_asdu_func";
+
+//-------------------------------------------------------------------------
+// func lookup
+//-------------------------------------------------------------------------
+
+struct Iec104AsduFuncMap
+{
+ const char* name;
+ uint8_t func;
+};
+
+/* Mapping of name -> function code for 'iec104_asdu_func' option. */
+static Iec104AsduFuncMap iec104_asdu_func_map[] =
+{
+ { "reserved", IEC104_NO_ASDU }, // 0 reserved
+ { "M_SP_NA_1", IEC104_ASDU_M_SP_NA_1 }, { "m_sp_na_1", IEC104_ASDU_M_SP_NA_1 }, // Single-point information
+ { "M_SP_TA_1", IEC104_ASDU_M_SP_TA_1 }, { "m_sp_ta_1", IEC104_ASDU_M_SP_TA_1 }, // Single-point information with time tag
+ { "M_DP_NA_1", IEC104_ASDU_M_DP_NA_1 }, { "m_dp_na_1", IEC104_ASDU_M_DP_NA_1 }, // Double-point information
+ { "M_DP_TA_1", IEC104_ASDU_M_DP_TA_1 }, { "m_dp_ta_1", IEC104_ASDU_M_DP_TA_1 }, // Double-point information with time tag
+ { "M_ST_NA_1", IEC104_ASDU_M_ST_NA_1 }, { "m_st_na_1", IEC104_ASDU_M_ST_NA_1 }, // Step position information
+ { "M_ST_TA_1", IEC104_ASDU_M_ST_TA_1 }, { "m_st_ta_1", IEC104_ASDU_M_ST_TA_1 }, // Step position information with time tag
+ { "M_BO_NA_1", IEC104_ASDU_M_BO_NA_1 }, { "m_bo_na_1", IEC104_ASDU_M_BO_NA_1 }, // Bitstring of 32 bit
+ { "M_BO_TA_1", IEC104_ASDU_M_BO_TA_1 }, { "m_bo_ta_1", IEC104_ASDU_M_BO_TA_1 }, // Bitstring of 32 bit with time tag
+ { "M_ME_NA_1", IEC104_ASDU_M_ME_NA_1 }, { "m_me_na_1", IEC104_ASDU_M_ME_NA_1 }, // Measured value, normalized value
+ { "M_ME_TA_1", IEC104_ASDU_M_ME_TA_1 }, { "m_me_ta_1", IEC104_ASDU_M_ME_TA_1 }, // Measured value, normalized value with time tag
+ { "M_ME_NB_1", IEC104_ASDU_M_ME_NB_1 }, { "m_me_nb_1", IEC104_ASDU_M_ME_NB_1 }, // Measured value, scaled value
+ { "M_ME_TB_1", IEC104_ASDU_M_ME_TB_1 }, { "m_me_tb_1", IEC104_ASDU_M_ME_TB_1 }, // Measured value, scaled value wit time tag
+ { "M_ME_NC_1", IEC104_ASDU_M_ME_NC_1 }, { "m_me_nc_1", IEC104_ASDU_M_ME_NC_1 }, // Measured value, short floating point number
+ { "M_ME_TC_1", IEC104_ASDU_M_ME_TC_1 }, { "m_me_tc_1", IEC104_ASDU_M_ME_TC_1 }, // Measured value, short floating point number with time tag
+ { "M_IT_NA_1", IEC104_ASDU_M_IT_NA_1 }, { "m_it_na_1", IEC104_ASDU_M_IT_NA_1 }, // Integrated totals
+ { "M_IT_TA_1", IEC104_ASDU_M_IT_TA_1 }, { "m_it_ta_1", IEC104_ASDU_M_IT_TA_1 }, // Integrated totals with time tag
+ { "M_EP_TA_1", IEC104_ASDU_M_EP_TA_1 }, { "m_ep_ta_1", IEC104_ASDU_M_EP_TA_1 }, // Event of protection equipment with time tag
+ { "M_EP_TB_1", IEC104_ASDU_M_EP_TB_1 }, { "m_ep_tb_1", IEC104_ASDU_M_EP_TB_1 }, // Packed start events of protection equipment with time tag
+ { "M_EP_TC_1", IEC104_ASDU_M_EP_TC_1 }, { "m_ep_tc_1", IEC104_ASDU_M_EP_TC_1 }, // Packed output circuit information of protection equipment with time tag
+ { "M_PS_NA_1", IEC104_ASDU_M_PS_NA_1 }, { "m_ps_na_1", IEC104_ASDU_M_PS_NA_1 }, // Packed single point information with status change detection
+ { "M_ME_ND_1", IEC104_ASDU_M_ME_ND_1 }, { "m_me_nd_1", IEC104_ASDU_M_ME_ND_1 }, // Measured value, normalized value without quality descriptor
+ // 22-29 reserved
+ { "M_SP_TB_1", IEC104_ASDU_M_SP_TB_1 }, { "m_sp_tb_1", IEC104_ASDU_M_SP_TB_1 }, // Single-point information with time tag CP56Time2a
+ { "M_DP_TB_1", IEC104_ASDU_M_DP_TB_1 }, { "m_dp_tb_1", IEC104_ASDU_M_DP_TB_1 }, // Double-point information with time tag CP56Time2a
+ { "M_ST_TB_1", IEC104_ASDU_M_ST_TB_1 }, { "m_st_tb_1", IEC104_ASDU_M_ST_TB_1 }, // Step position information with time tag CP56Time2a
+ { "M_BO_TB_1", IEC104_ASDU_M_BO_TB_1 }, { "m_bo_tb_1", IEC104_ASDU_M_BO_TB_1 }, // Bitstring of 32 bit with time tag CP56Time2a
+ { "M_ME_TD_1", IEC104_ASDU_M_ME_TD_1 }, { "m_me_td_1", IEC104_ASDU_M_ME_TD_1 }, // Measured value, normalized value with time tag CP56Time2a
+ { "M_ME_TE_1", IEC104_ASDU_M_ME_TE_1 }, { "m_me_te_1", IEC104_ASDU_M_ME_TE_1 }, // Measured value, scaled value with time tag CP56Time2a
+ { "M_ME_TF_1", IEC104_ASDU_M_ME_TF_1 }, { "m_me_tf_1", IEC104_ASDU_M_ME_TF_1 }, // Measured value, short floating point number with time tag CP56Time2a
+ { "M_IT_TB_1", IEC104_ASDU_M_IT_TB_1 }, { "m_it_tb_1", IEC104_ASDU_M_IT_TB_1 }, // Integrated totals with time tag CP56Time2a
+ { "M_EP_TD_1", IEC104_ASDU_M_EP_TD_1 }, { "m_ep_td_1", IEC104_ASDU_M_EP_TD_1 }, // Event of protection equipment with time tag CP56Time2a
+ { "M_EP_TE_1", IEC104_ASDU_M_EP_TE_1 }, { "m_ep_te_1", IEC104_ASDU_M_EP_TE_1 }, // Packed start events of protection equipment with time tag CP56Time2a
+ { "M_EP_TF_1", IEC104_ASDU_M_EP_TF_1 }, { "m_ep_tf_1", IEC104_ASDU_M_EP_TF_1 }, // Packed output circuit information of protection equipment with time tag CP56Time2a
+ // 41-44 reserved
+ { "C_SC_NA_1", IEC104_ASDU_C_SC_NA_1 }, { "c_sc_na_1", IEC104_ASDU_C_SC_NA_1 }, // Single command
+ { "C_DC_NA_1", IEC104_ASDU_C_DC_NA_1 }, { "c_dc_na_1", IEC104_ASDU_C_DC_NA_1 }, // Double command
+ { "C_RC_NA_1", IEC104_ASDU_C_RC_NA_1 }, { "c_rc_na_1", IEC104_ASDU_C_RC_NA_1 }, // Regulating step command
+ { "C_SE_NA_1", IEC104_ASDU_C_SE_NA_1 }, { "c_se_na_1", IEC104_ASDU_C_SE_NA_1 }, // Set-point Command, normalized value
+ { "C_SE_NB_1", IEC104_ASDU_C_SE_NB_1 }, { "c_se_nb_1", IEC104_ASDU_C_SE_NB_1 }, // Set-point Command, scaled value
+ { "C_SE_NC_1", IEC104_ASDU_C_SE_NC_1 }, { "c_se_nc_1", IEC104_ASDU_C_SE_NC_1 }, // Set-point Command, short floating point number
+ { "C_BO_NA_1", IEC104_ASDU_C_BO_NA_1 }, { "c_bo_na_1", IEC104_ASDU_C_BO_NA_1 }, // Bitstring 32 bit command
+ // 52-57 reserved
+ { "C_SC_TA_1", IEC104_ASDU_C_SC_TA_1 }, { "c_sc_ta_1", IEC104_ASDU_C_SC_TA_1 }, // Single command with time tag CP56Time2a
+ { "C_DC_TA_1", IEC104_ASDU_C_DC_TA_1 }, { "c_dc_ta_1", IEC104_ASDU_C_DC_TA_1 }, // Double command with time tag CP56Time2a
+ { "C_RC_TA_1", IEC104_ASDU_C_RC_TA_1 }, { "c_rc_ta_1", IEC104_ASDU_C_RC_TA_1 }, // Regulating step command with time tag CP56Time2a
+ { "C_SE_TA_1", IEC104_ASDU_C_SE_TA_1 }, { "c_se_ta_1", IEC104_ASDU_C_SE_TA_1 }, // Set-point command with time tag CP56Time2a, normalized value
+ { "C_SE_TB_1", IEC104_ASDU_C_SE_TB_1 }, { "c_se_tb_1", IEC104_ASDU_C_SE_TB_1 }, // Set-point command with time tag CP56Time2a, scaled value
+ { "C_SE_TC_1", IEC104_ASDU_C_SE_TC_1 }, { "c_se_tc_1", IEC104_ASDU_C_SE_TC_1 }, // Set-point command with time tag CP56Time2a, short floating point number
+ { "C_BO_TA_1", IEC104_ASDU_C_BO_TA_1 }, { "c_bo_ta_1", IEC104_ASDU_C_BO_TA_1 }, // Bitstring of 32 bit with time tag CP56Time2a
+ // 65-69 reserved
+ { "M_EI_NA_1", IEC104_ASDU_M_EI_NA_1 }, { "m_ei_na_1", IEC104_ASDU_M_EI_NA_1 }, // End of initialization
+ // 71-99 reserved
+ { "C_IC_NA_1", IEC104_ASDU_C_IC_NA_1 }, { "c_ic_na_1", IEC104_ASDU_C_IC_NA_1 }, // Interrogation command
+ { "C_CI_NA_1", IEC104_ASDU_C_CI_NA_1 }, { "c_ci_na_1", IEC104_ASDU_C_CI_NA_1 }, // Counter interrogation command
+ { "C_RD_NA_1", IEC104_ASDU_C_RD_NA_1 }, { "c_rd_na_1", IEC104_ASDU_C_RD_NA_1 }, // Read command
+ { "C_CS_NA_1", IEC104_ASDU_C_CS_NA_1 }, { "c_cs_na_1", IEC104_ASDU_C_CS_NA_1 }, // Clock synchronization command
+ { "C_TS_NA_1", IEC104_ASDU_C_TS_NA_1 }, { "c_ts_na_1", IEC104_ASDU_C_TS_NA_1 }, // Test command
+ { "C_RP_NA_1", IEC104_ASDU_C_RP_NA_1 }, { "c_rp_na_1", IEC104_ASDU_C_RP_NA_1 }, // Reset process command
+ { "C_CD_NA_1", IEC104_ASDU_C_CD_NA_1 }, { "c_cd_na_1", IEC104_ASDU_C_CD_NA_1 }, // Delay acquisition command
+ { "C_TS_TA_1", IEC104_ASDU_C_TS_TA_1 }, { "c_ts_ta_1", IEC104_ASDU_C_TS_TA_1 }, // Test command with time tag CP56Time2a
+ // 108-109 reserved
+ { "P_ME_NA_1", IEC104_ASDU_P_ME_NA_1 }, { "p_me_na_1", IEC104_ASDU_P_ME_NA_1 }, // Parameter of measured values, normalized value
+ { "P_ME_NB_1", IEC104_ASDU_P_ME_NB_1 }, { "p_me_nb_1", IEC104_ASDU_P_ME_NB_1 }, // Parameter of measured values, scaled value
+ { "P_ME_NC_1", IEC104_ASDU_P_ME_NC_1 }, { "p_me_nc_1", IEC104_ASDU_P_ME_NC_1 }, // Parameter of measured values, short floating point number
+ { "P_AC_NA_1", IEC104_ASDU_P_AC_NA_1 }, { "p_ac_na_1", IEC104_ASDU_P_AC_NA_1 }, // Parameter activation
+ // 114-119 reserved
+ { "F_FR_NA_1", IEC104_ASDU_F_FR_NA_1 }, { "f_fr_na_1", IEC104_ASDU_F_FR_NA_1 }, // File ready
+ { "F_SR_NA_1", IEC104_ASDU_F_SR_NA_1 }, { "f_sr_na_1", IEC104_ASDU_F_SR_NA_1 }, // Section ready
+ { "F_SC_NA_1", IEC104_ASDU_F_SC_NA_1 }, { "f_sc_na_1", IEC104_ASDU_F_SC_NA_1 }, // Call directory, select file, call file, call section
+ { "F_LS_NA_1", IEC104_ASDU_F_LS_NA_1 }, { "f_ls_na_1", IEC104_ASDU_F_LS_NA_1 }, // Last section, last segment
+ { "F_AF_NA_1", IEC104_ASDU_F_AF_NA_1 }, { "f_af_na_1", IEC104_ASDU_F_AF_NA_1 }, // ACK file, ACK section
+ { "F_SG_NA_1", IEC104_ASDU_F_SG_NA_1 }, { "f_sg_na_1", IEC104_ASDU_F_SG_NA_1 }, // Single information object
+ { "F_DR_TA_1", IEC104_ASDU_F_DR_TA_1 }, { "f_dr_ta_1", IEC104_ASDU_F_DR_TA_1 }, // Sequence of information elements in a single information object
+ { "F_SC_NB_1", IEC104_ASDU_F_SC_NB_1 }, { "f_sc_nb_1", IEC104_ASDU_F_SC_NB_1 }, // QueryLog – Request archive file
+ // 128-256 reserved
+ };
+
+static bool get_func(const char* s, long &n)
+{
+ constexpr size_t max = (sizeof(iec104_asdu_func_map) / sizeof(Iec104AsduFuncMap));
+
+ for (size_t i = 0; i < max; ++i)
+ {
+ if (!strcmp(s, iec104_asdu_func_map[i].name))
+ {
+ n = iec104_asdu_func_map[i].func;
+ return true;
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------------------------
+// func option
+//-------------------------------------------------------------------------
+
+static THREAD_LOCAL ProfileStats iec104_asdu_func_prof;
+
+class Iec104AsduFuncOption: public IpsOption
+{
+public:
+ Iec104AsduFuncOption(uint16_t v) :
+ IpsOption(s_name)
+ {
+ func = v;
+ }
+
+ uint32_t hash() const override;
+ bool operator==(const IpsOption&) const override;
+
+ EvalStatus eval(Cursor&, Packet*) override;
+
+public:
+ uint16_t func;
+};
+
+uint32_t Iec104AsduFuncOption::hash() const
+{
+ uint32_t a = func, b = IpsOption::hash(), c = 0;
+
+ mix(a, b, c);
+ finalize(a, b, c);
+
+ return c;
+}
+
+bool Iec104AsduFuncOption::operator==(const IpsOption &ips) const
+{
+ if (!IpsOption::operator==(ips))
+ {
+ return false;
+ }
+
+ const Iec104AsduFuncOption &rhs = (const Iec104AsduFuncOption&) ips;
+ return (func == rhs.func);
+}
+
+IpsOption::EvalStatus Iec104AsduFuncOption::eval(Cursor&, Packet* p)
+{
+ RuleProfile profile(iec104_asdu_func_prof);
+
+ if (!p->flow)
+ {
+ return NO_MATCH;
+ }
+
+ if (!p->is_full_pdu())
+ {
+ return NO_MATCH;
+ }
+
+ // check if the packet function matches the rule option function
+ Iec104FlowData* iec104fd = (Iec104FlowData*) p->flow->get_flow_data(Iec104FlowData::inspector_id);
+
+ // ASDU only occurs in APCI type I
+ if (iec104fd and iec104fd->ssn_data.iec104_apci_type == IEC104_APCI_TYPE_I)
+ {
+ // alert only when the target function matches the existing function
+ if (func == iec104fd->ssn_data.iec104_asdu_func)
+ {
+ return MATCH;
+ }
+ }
+
+ return NO_MATCH;
+}
+
+//-------------------------------------------------------------------------
+// module
+//-------------------------------------------------------------------------
+
+static const Parameter s_params[] =
+{
+ { "~", Parameter::PT_STRING, nullptr, nullptr,
+ "function code to match" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+#define s_help \
+ "rule option to check iec104 function code"
+
+class Iec104AsduFuncModule: public Module
+{
+public:
+ Iec104AsduFuncModule() :
+ Module(s_name, s_help, s_params)
+ {
+ }
+
+ bool set(const char*, Value&, SnortConfig*) override;
+
+ ProfileStats* get_profile() const override
+ {
+ return &iec104_asdu_func_prof;
+ }
+
+ Usage get_usage() const override
+ {
+ return DETECT;
+ }
+
+public:
+ uint8_t func = IEC104_NO_ASDU;
+};
+
+bool Iec104AsduFuncModule::set(const char*, Value &v, SnortConfig*)
+{
+ if (!v.is("~"))
+ {
+ return false;
+ }
+
+ long n;
+
+ if (v.strtol(n))
+ {
+ func = static_cast<uint8_t>(n);
+ }
+
+ else if (get_func(v.get_string(), n))
+ {
+ func = static_cast<uint8_t>(n);
+ }
+
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------
+// api
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{
+ return new Iec104AsduFuncModule;
+}
+
+static void mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static IpsOption* opt_ctor(Module* m, OptTreeNode*)
+{
+ Iec104AsduFuncModule* mod = (Iec104AsduFuncModule*) m;
+ return new Iec104AsduFuncOption(mod->func);
+}
+
+static void opt_dtor(IpsOption* p)
+{
+ delete p;
+}
+
+static const IpsApi ips_api =
+{
+ {
+ PT_IPS_OPTION,
+ sizeof(IpsApi),
+ IPSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ s_name,
+ s_help,
+ mod_ctor,
+ mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0,
+ PROTO_BIT__TCP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ opt_ctor,
+ opt_dtor,
+ nullptr
+};
+
+const BaseApi* ips_iec104_asdu_func = &ips_api.base;
+
extern const BaseApi* sin_dce[];
extern const BaseApi* sin_dnp3[];
extern const BaseApi* sin_gtp[];
+extern const BaseApi* sin_iec104[];
extern const BaseApi* sin_modbus[];
extern const BaseApi* sin_netflow[];
extern const BaseApi* sin_s7commplus[];
PluginManager::load_plugins(sin_dce);
PluginManager::load_plugins(sin_dnp3);
PluginManager::load_plugins(sin_gtp);
+ PluginManager::load_plugins(sin_iec104);
PluginManager::load_plugins(sin_modbus);
PluginManager::load_plugins(sin_netflow);
PluginManager::load_plugins(sin_s7commplus);