From: Mike Stepanek (mstepane) Date: Wed, 24 Feb 2021 02:25:20 +0000 (+0000) Subject: Merge pull request #2743 in SNORT/snort3 from ~JRITTLE/snort3:iec104_service_inspecto... X-Git-Tag: 3.1.2.0~28 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b2a4912dbeb459a25c71778775499b42e815e40e;p=thirdparty%2Fsnort3.git Merge pull request #2743 in SNORT/snort3 from ~JRITTLE/snort3:iec104_service_inspector to master Squashed commit of the following: commit 4f3019db2c8f24111cbf99e154feb30f1876ef70 Author: jrittle Date: Tue Feb 23 14:20:42 2021 -0500 iec104: integrating new iec104 protocol service inspector --- diff --git a/lua/snort.lua b/lua/snort.lua index 30a55c4ea..dc9df2c8a 100644 --- a/lua/snort.lua +++ b/lua/snort.lua @@ -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' } }, diff --git a/src/service_inspectors/CMakeLists.txt b/src/service_inspectors/CMakeLists.txt index cc465800d..01343fb21 100644 --- a/src/service_inspectors/CMakeLists.txt +++ b/src/service_inspectors/CMakeLists.txt @@ -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) $ $ $ + $ $ $ $ diff --git a/src/service_inspectors/iec104/CMakeLists.txt b/src/service_inspectors/iec104/CMakeLists.txt new file mode 100644 index 000000000..76f54dcb6 --- /dev/null +++ b/src/service_inspectors/iec104/CMakeLists.txt @@ -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 index 000000000..7d5597ab3 --- /dev/null +++ b/src/service_inspectors/iec104/dev_notes.txt @@ -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 index 000000000..978c51832 --- /dev/null +++ b/src/service_inspectors/iec104/iec104.cc @@ -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 +// modeled after modbus.cc (author Russ Combs ) +// modeled after s7comm.cc (author Pradeep Damodharan ) + +#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 index 000000000..cce2fb34a --- /dev/null +++ b/src/service_inspectors/iec104/iec104.h @@ -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 +// modeled after modbus.h (author Russ Combs ) +// modeled after s7comm.h (author Pradeep Damodharan ) + +#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 index 000000000..81f683398 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_decode.cc @@ -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 +// modeled after modbus_decode.cc (author Russ Combs ) +// modeled after s7comm_decode.cc (author Pradeep Damodharan ) + +#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 index 000000000..50873ced9 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_decode.h @@ -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 +// modeled after modbus_decode.h (author Russ Combs ) +// modeled after s7comm_decode.h (author Pradeep Damodharan ) + +#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 index 000000000..e549255f6 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_module.cc @@ -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 +// modeled after modbus_module.c (author Russ Combs ) +// modeled after s7comm_module.c (author Pradeep Damodharan ) + +#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 index 000000000..37ef3245c --- /dev/null +++ b/src/service_inspectors/iec104/iec104_module.h @@ -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 +// modeled after modbus_module.h (author Russ Combs ) +// modeled after s7comm_module.h (author Pradeep Damodharan ) + +#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 index 000000000..4c52c90f7 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_paf.cc @@ -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 +// modeled after modbus_paf.cc (author Ryan Jordan) +// modeled after s7comm_paf.cc (author Pradeep Damodharan ) + +// 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 index 000000000..141296781 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_paf.h @@ -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 +// modeled after modbus_paf.h (author Russ Combs ) +// modeled after s7comm_paf.h (author Pradeep Damodharan ) + +#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 index 000000000..f7a852122 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_parse_apdu.cc @@ -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 + +#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 index 000000000..22246beba --- /dev/null +++ b/src/service_inspectors/iec104/iec104_parse_apdu.h @@ -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 + +#ifndef IEC104_PARSE_APCI_H +#define IEC104_PARSE_APCI_H + +#include + +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 index 000000000..f956ed4b1 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_parse_information_object_elements.cc @@ -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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "iec104_parse_information_object_elements.h" + +#include + +#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 index 000000000..2857b6da3 --- /dev/null +++ b/src/service_inspectors/iec104/iec104_parse_information_object_elements.h @@ -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 + +#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 index 000000000..5bd7acade --- /dev/null +++ b/src/service_inspectors/iec104/iec104_trace.h @@ -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 +// modeled after detect_trace.cc (author Maya Dagon ) + +#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 index 000000000..27b42d4b1 --- /dev/null +++ b/src/service_inspectors/iec104/ips_iec104_apci_type.cc @@ -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 +// modeled after ips_modbus_func.cc (author Russ Combs ) + +#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(n); + } + + else if (get_apci_type(v.get_string(), n)) + { + apci_type = static_cast(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 index 000000000..7489e9afc --- /dev/null +++ b/src/service_inspectors/iec104/ips_iec104_asdu_func.cc @@ -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 +// modeled after ips_modbus_func.cc (author Russ Combs ) + +#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(n); + } + + else if (get_func(v.get_string(), n)) + { + func = static_cast(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; + diff --git a/src/service_inspectors/service_inspectors.cc b/src/service_inspectors/service_inspectors.cc index 8d2038c76..cc30b30a0 100644 --- a/src/service_inspectors/service_inspectors.cc +++ b/src/service_inspectors/service_inspectors.cc @@ -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);