]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2743 in SNORT/snort3 from ~JRITTLE/snort3:iec104_service_inspecto...
authorMike Stepanek (mstepane) <mstepane@cisco.com>
Wed, 24 Feb 2021 02:25:20 +0000 (02:25 +0000)
committerMike Stepanek (mstepane) <mstepane@cisco.com>
Wed, 24 Feb 2021 02:25:20 +0000 (02:25 +0000)
Squashed commit of the following:

commit 4f3019db2c8f24111cbf99e154feb30f1876ef70
Author: jrittle <jrittle@cisco.com>
Date:   Tue Feb 23 14:20:42 2021 -0500

    iec104: integrating new iec104 protocol service inspector

20 files changed:
lua/snort.lua
src/service_inspectors/CMakeLists.txt
src/service_inspectors/iec104/CMakeLists.txt [new file with mode: 0644]
src/service_inspectors/iec104/dev_notes.txt [new file with mode: 0644]
src/service_inspectors/iec104/iec104.cc [new file with mode: 0644]
src/service_inspectors/iec104/iec104.h [new file with mode: 0644]
src/service_inspectors/iec104/iec104_decode.cc [new file with mode: 0644]
src/service_inspectors/iec104/iec104_decode.h [new file with mode: 0644]
src/service_inspectors/iec104/iec104_module.cc [new file with mode: 0644]
src/service_inspectors/iec104/iec104_module.h [new file with mode: 0644]
src/service_inspectors/iec104/iec104_paf.cc [new file with mode: 0644]
src/service_inspectors/iec104/iec104_paf.h [new file with mode: 0644]
src/service_inspectors/iec104/iec104_parse_apdu.cc [new file with mode: 0644]
src/service_inspectors/iec104/iec104_parse_apdu.h [new file with mode: 0644]
src/service_inspectors/iec104/iec104_parse_information_object_elements.cc [new file with mode: 0644]
src/service_inspectors/iec104/iec104_parse_information_object_elements.h [new file with mode: 0644]
src/service_inspectors/iec104/iec104_trace.h [new file with mode: 0644]
src/service_inspectors/iec104/ips_iec104_apci_type.cc [new file with mode: 0644]
src/service_inspectors/iec104/ips_iec104_asdu_func.cc [new file with mode: 0644]
src/service_inspectors/service_inspectors.cc

index 30a55c4eae19e564d3ff5cf67a6bc0ccd1277783..dc9df2c8a9326edcb3115545d765eb2d0b70ac3f 100644 (file)
@@ -57,6 +57,7 @@ dns = { }
 http_inspect = { }
 http2_inspect = { }
 imap = { }
+iec104 = { }
 modbus = { }
 netflow = {}
 normalizer = { }
@@ -116,6 +117,7 @@ binder =
     { 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' } },
@@ -133,6 +135,7 @@ binder =
     { 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' } },
index cc465800dcbcc315ed59fee695011160d29e5d89..01343fb21e6e82a445c4c66bc7500d1ef6652d67 100644 (file)
@@ -8,6 +8,7 @@ add_subdirectory(ftp_telnet)
 add_subdirectory(gtp)
 add_subdirectory(http_inspect)
 add_subdirectory(http2_inspect)
+add_subdirectory(iec104)
 add_subdirectory(imap)
 add_subdirectory(modbus)
 add_subdirectory(netflow)
@@ -29,6 +30,7 @@ if (STATIC_INSPECTORS)
         $<TARGET_OBJECTS:dns>
         $<TARGET_OBJECTS:ftp_telnet>
         $<TARGET_OBJECTS:gtp_inspect>
+        $<TARGET_OBJECTS:iec104>
         $<TARGET_OBJECTS:imap>
         $<TARGET_OBJECTS:modbus>
         $<TARGET_OBJECTS:netflow>
diff --git a/src/service_inspectors/iec104/CMakeLists.txt b/src/service_inspectors/iec104/CMakeLists.txt
new file mode 100644 (file)
index 0000000..76f54dc
--- /dev/null
@@ -0,0 +1,26 @@
+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)
+
diff --git a/src/service_inspectors/iec104/dev_notes.txt b/src/service_inspectors/iec104/dev_notes.txt
new file mode 100644 (file)
index 0000000..7d5597a
--- /dev/null
@@ -0,0 +1,17 @@
+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
diff --git a/src/service_inspectors/iec104/iec104.cc b/src/service_inspectors/iec104/iec104.cc
new file mode 100644 (file)
index 0000000..978c518
--- /dev/null
@@ -0,0 +1,215 @@
+//--------------------------------------------------------------------------
+// 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
+};
+
diff --git a/src/service_inspectors/iec104/iec104.h b/src/service_inspectors/iec104/iec104.h
new file mode 100644 (file)
index 0000000..cce2fb3
--- /dev/null
@@ -0,0 +1,80 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/iec104_decode.cc b/src/service_inspectors/iec104/iec104_decode.cc
new file mode 100644 (file)
index 0000000..81f6833
--- /dev/null
@@ -0,0 +1,161 @@
+//--------------------------------------------------------------------------
+// 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;
+}
+
diff --git a/src/service_inspectors/iec104/iec104_decode.h b/src/service_inspectors/iec104/iec104_decode.h
new file mode 100644 (file)
index 0000000..50873ce
--- /dev/null
@@ -0,0 +1,42 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/iec104_module.cc b/src/service_inspectors/iec104/iec104_module.cc
new file mode 100644 (file)
index 0000000..e549255
--- /dev/null
@@ -0,0 +1,155 @@
+//--------------------------------------------------------------------------
+// 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)
+{
+}
+
diff --git a/src/service_inspectors/iec104/iec104_module.h b/src/service_inspectors/iec104/iec104_module.h
new file mode 100644 (file)
index 0000000..37ef324
--- /dev/null
@@ -0,0 +1,185 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/iec104_paf.cc b/src/service_inspectors/iec104/iec104_paf.cc
new file mode 100644 (file)
index 0000000..4c52c90
--- /dev/null
@@ -0,0 +1,94 @@
+//--------------------------------------------------------------------------
+// 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;
+}
+
diff --git a/src/service_inspectors/iec104/iec104_paf.h b/src/service_inspectors/iec104/iec104_paf.h
new file mode 100644 (file)
index 0000000..1412967
--- /dev/null
@@ -0,0 +1,53 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/iec104_parse_apdu.cc b/src/service_inspectors/iec104/iec104_parse_apdu.cc
new file mode 100644 (file)
index 0000000..f7a8521
--- /dev/null
@@ -0,0 +1,4183 @@
+//--------------------------------------------------------------------------
+// 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;
+        }
+        }
+    }
+}
+
diff --git a/src/service_inspectors/iec104/iec104_parse_apdu.h b/src/service_inspectors/iec104/iec104_parse_apdu.h
new file mode 100644 (file)
index 0000000..22246be
--- /dev/null
@@ -0,0 +1,2032 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/iec104_parse_information_object_elements.cc b/src/service_inspectors/iec104/iec104_parse_information_object_elements.cc
new file mode 100644 (file)
index 0000000..f956ed4
--- /dev/null
@@ -0,0 +1,1165 @@
+//--------------------------------------------------------------------------
+// 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
+    }
+}
+
diff --git a/src/service_inspectors/iec104/iec104_parse_information_object_elements.h b/src/service_inspectors/iec104/iec104_parse_information_object_elements.h
new file mode 100644 (file)
index 0000000..2857b6d
--- /dev/null
@@ -0,0 +1,2163 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/iec104_trace.h b/src/service_inspectors/iec104/iec104_trace.h
new file mode 100644 (file)
index 0000000..5bd7aca
--- /dev/null
@@ -0,0 +1,51 @@
+//--------------------------------------------------------------------------
+// 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
+
diff --git a/src/service_inspectors/iec104/ips_iec104_apci_type.cc b/src/service_inspectors/iec104/ips_iec104_apci_type.cc
new file mode 100644 (file)
index 0000000..27b42d4
--- /dev/null
@@ -0,0 +1,265 @@
+//--------------------------------------------------------------------------
+// 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;
+
diff --git a/src/service_inspectors/iec104/ips_iec104_asdu_func.cc b/src/service_inspectors/iec104/ips_iec104_asdu_func.cc
new file mode 100644 (file)
index 0000000..7489e9a
--- /dev/null
@@ -0,0 +1,338 @@
+//--------------------------------------------------------------------------
+// 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;
+
index 8d2038c76a6d178252c6f4e9ca79475f9c4a498b..cc30b30a089c9f4a35d384ff3422b91ba04d26e7 100644 (file)
@@ -52,6 +52,7 @@ extern const BaseApi* sin_cip[];
 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[];
@@ -92,6 +93,7 @@ void load_service_inspectors()
     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);