diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
new file mode 100644
index 00000000..d52fd077
--- /dev/null
+++ b/.github/workflows/cifuzz.yml
@@ -0,0 +1,26 @@
+name: CIFuzz
+on: [pull_request]
+jobs:
+ Fuzzing:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Build Fuzzers
+ id: build
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libiec61850'
+ dry-run: false
+ language: c
+ - name: Run Fuzzers
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ with:
+ oss-fuzz-project-name: 'libiec61850'
+ fuzz-seconds: 300
+ dry-run: false
+ language: c
+ - name: Upload Crash
+ uses: actions/upload-artifact@v3
+ if: failure() && steps.build.outcome == 'success'
+ with:
+ name: artifacts
+ path: ./out/artifacts
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a538fb97..7499c8c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,13 +33,16 @@ option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF)
option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" ON)
option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF)
+set(CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS 5 CACHE STRING "Configure the maximum number of get file tasks")
+set(CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 100 CACHE STRING "Configure the maximum number of dataSet members")
+
option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON)
option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON)
option(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB "Build with pre-compiled mbedtls dynamic library" OFF)
-set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" )
-set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include" CACHE STRING "Path to search for the mbedtls include files" )
+set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" )
+set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include" CACHE STRING "Path to search for the mbedtls include files" )
# choose the library features which shall be included
option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control features" ON)
@@ -53,6 +56,8 @@ option(CONFIG_IEC61850_R_GOOSE "Build with support for R-GOOSE (mbedtls required
option(CONFIG_IEC61850_R_SMV "Build with support for R-SMV (mbedtls required)" ON)
option(CONFIG_IEC61850_SNTP_CLIENT "Build with SNTP client code" ON)
+set(CONFIG_IEC61850_SG_RESVTMS 300 CACHE STRING "Configure the maximum number of SG RESVTMS")
+
set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" )
# advanced options
@@ -132,10 +137,15 @@ set(USE_PREBUILD_MBEDTLS 1)
set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH})
endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
-if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16)
+if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h)
+set(FOUND_SQLITE3_SOURCE 1)
+message("Found sqlite3 source in third_party folder -> can compile with log service support")
+endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h)
+
+if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28)
set(WITH_MBEDTLS 1)
-set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include")
-endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16)
+set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include")
+endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28)
if(WITH_MBEDTLS)
@@ -162,6 +172,11 @@ if (SUPPORT_REDUNDANT_DECLS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls")
endif(SUPPORT_REDUNDANT_DECLS)
+check_c_compiler_flag("-Wundef" SUPPORT_UNDEF)
+if (SUPPORT_UNDEF)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wundef")
+endif(SUPPORT_UNDEF)
+
# write the detected stuff to this file
configure_file(
${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake
diff --git a/Makefile b/Makefile
index db06c7f8..ef530346 100644
--- a/Makefile
+++ b/Makefile
@@ -78,9 +78,9 @@ LIB_INCLUDE_DIRS += third_party/winpcap/Include
endif
ifdef WITH_MBEDTLS
-LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.16/library
+LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.28/library
LIB_SOURCE_DIRS += hal/tls/mbedtls
-LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.16/include
+LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.28/include
LIB_INCLUDE_DIRS += hal/tls/mbedtls
CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"'
CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1'
diff --git a/README.md b/README.md
index 08139382..d8024cff 100644
--- a/README.md
+++ b/README.md
@@ -88,9 +88,9 @@ You can test the server examples by using a generic client or the provided clien
## Building the library with TLS support
-Download, unpack, and copy mbedtls-2.16 into the third_party/mbedtls folder.
+Download, unpack, and copy mbedtls-2.28 into the third_party/mbedtls folder.
-NOTE: The current version support mbedtls version 2.16. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.16".
+NOTE: The current version support mbedtls version 2.28. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.28".
In the main libiec61850 folder run
@@ -98,7 +98,7 @@ In the main libiec61850 folder run
make WITH_MBEDTLS=1
```
-When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.16 is present.
+When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.28 is present.
## Installing the library and the API headers
diff --git a/config/stack_config.h b/config/stack_config.h
index 5518b9f0..3cd3e771 100644
--- a/config/stack_config.h
+++ b/config/stack_config.h
@@ -238,9 +238,6 @@
#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0
-/* use short FC defines as in old API */
-#define CONFIG_PROVIDE_OLD_FC_DEFINES 0
-
/* Support user access to raw messages */
#define CONFIG_MMS_RAW_MESSAGE_LOGGING 1
diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake
index 2901316d..52017dc2 100644
--- a/config/stack_config.h.cmake
+++ b/config/stack_config.h.cmake
@@ -155,7 +155,7 @@
#cmakedefine01 CONFIG_IEC61850_SETTING_GROUPS
/* default reservation time of a setting group control block in s */
-#define CONFIG_IEC61850_SG_RESVTMS 100
+#cmakedefine CONFIG_IEC61850_SG_RESVTMS @CONFIG_IEC61850_SG_RESVTMS@
/* include support for IEC 61850 log services */
#cmakedefine01 CONFIG_IEC61850_LOG_SERVICE
@@ -196,7 +196,10 @@
#define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10
/* Maximum number of the members in a data set (named variable list) */
-#define CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 50
+#cmakedefine CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS @CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS@
+
+/* Maximum number of get file tasks */
+#cmakedefine CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS @CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS@
/* Definition of supported services */
#define MMS_DEFAULT_PROFILE 1
@@ -224,9 +227,6 @@
#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0
-/* use short FC defines as in old API */
-#define CONFIG_PROVIDE_OLD_FC_DEFINES 0
-
/* Support user acccess to raw messages */
#cmakedefine01 CONFIG_MMS_RAW_MESSAGE_LOGGING
diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs
index b3c2bcc9..97a3d767 100644
--- a/dotnet/IEC61850forCSharp/Control.cs
+++ b/dotnet/IEC61850forCSharp/Control.cs
@@ -775,7 +775,10 @@ namespace IEC61850
Dispose (true);
}
-
+ ~ControlObject()
+ {
+ Dispose (false);
+ }
}
}
diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
index 1403cbec..16d79a56 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs
@@ -1,7 +1,7 @@
/*
* IEC61850ClientAPI.cs
*
- * Copyright 2014-2021 Michael Zillgith
+ * Copyright 2014-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -437,6 +437,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedConnection_setLocalAddress(IntPtr self, string localIpAddress, int localPort);
+
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_abort(IntPtr self, out int error);
@@ -894,6 +897,25 @@ namespace IEC61850
Connect(hostname, -1);
}
+ ///
+ /// Set the local IP address and port to be used by the client
+ ///
+ /// the local IP address or hostname
+ /// the local TCP port to use. When 0 the OS will chose the TCP port to use.
+ public void SetLocalAddress(string localIpAddress, int localPort)
+ {
+ IedConnection_setLocalAddress(connection, localIpAddress, localPort);
+ }
+
+ ///
+ /// Set the local IP address to be used by the client
+ ///
+ /// the local IP address or hostname
+ public void SetLocalAddress(string localIpAddress)
+ {
+ IedConnection_setLocalAddress(connection, localIpAddress, 0);
+ }
+
/// This exception is thrown if there is a connection or service error
public ControlObject CreateControlObject(string objectReference)
{
@@ -1993,7 +2015,6 @@ namespace IEC61850
if (accessResults != IntPtr.Zero)
{
-
IntPtr element = LinkedList_getNext(accessResults);
while (element != IntPtr.Zero)
@@ -2004,6 +2025,9 @@ namespace IEC61850
MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError();
+ if (accessResultList == null)
+ accessResultList = new List();
+
accessResultList.Add(dataAccessError);
element = LinkedList_getNext(element);
diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
index 5c6a40d6..b0d2c054 100644
--- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
+++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs
@@ -1866,6 +1866,14 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_isSelect(IntPtr self);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool ControlAction_getSynchroCheck(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool ControlAction_getInterlockCheck(IntPtr self);
+
private IntPtr self;
private IedServer.ControlHandlerInfo info;
private IedServer iedServer;
@@ -1985,6 +1993,16 @@ namespace IEC61850
{
return ControlAction_isSelect(self);
}
+
+ public bool GetSynchroCheck()
+ {
+ return ControlAction_getSynchroCheck(self);
+ }
+
+ public bool GetInterlockCheck()
+ {
+ return ControlAction_getInterlockCheck(self);
+ }
}
public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
@@ -2273,6 +2291,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter);
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void IedServer_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision);
+
private IntPtr self = IntPtr.Zero;
private InternalControlHandler internalControlHandlerRef = null;
@@ -2435,6 +2456,9 @@ namespace IEC61850
/* store IedModel instance to prevent garbage collector */
private IedModel iedModel = null;
+ /* store TLSConfiguration instance to prevent garbage collector */
+ private TLSConfiguration tlsConfiguration = null;
+
public IedServer(IedModel iedModel, IedServerConfig config = null)
{
this.iedModel = iedModel;
@@ -2450,6 +2474,7 @@ namespace IEC61850
public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
{
this.iedModel = iedModel;
+ this.tlsConfiguration = tlsConfig;
IntPtr nativeConfig = IntPtr.Zero;
IntPtr nativeTLSConfig = IntPtr.Zero;
@@ -2539,6 +2564,7 @@ namespace IEC61850
self = IntPtr.Zero;
internalConnectionHandler = null;
this.iedModel = null;
+ this.tlsConfiguration = null;
}
}
}
@@ -2996,6 +3022,18 @@ namespace IEC61850
}
}
+ ///
+ /// Set the time quality for all timestamps internally generated by this IedServer instance
+ ///
+ /// set/unset leap seconds known flag
+ /// set/unset clock failure flag
+ /// set/unset clock not synchronized flag
+ /// set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)
+ public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision)
+ {
+ IedServer_setTimeQuality(self, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision);
+ }
+
}
}
diff --git a/dotnet/IEC61850forCSharp/TLS.cs b/dotnet/IEC61850forCSharp/TLS.cs
index 69a273b0..05512a9b 100644
--- a/dotnet/IEC61850forCSharp/TLS.cs
+++ b/dotnet/IEC61850forCSharp/TLS.cs
@@ -1,7 +1,7 @@
/*
* TLS.cs
*
- * Copyright 2017 Michael Zillgith
+ * Copyright 2017-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -35,181 +35,400 @@ using IEC61850.Common;
///
namespace IEC61850
{
- namespace TLS
- {
- ///
- /// A container for TLS configuration and certificates.
- ///
- public class TLSConfiguration : IDisposable
- {
- private IntPtr self = IntPtr.Zero;
-
- private bool allowOnlyKnownCerts = false;
- private bool chainValidation = true;
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr TLSConfiguration_create();
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void TLSConfiguration_destroy(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void TLSConfiguration_setChainValidation (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- static extern void TLSConfiguration_setClientMode(IntPtr self);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_setOwnKeyFromFile (IntPtr self, string filename, string keyPassword);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen);
-
- [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
- [return: MarshalAs(UnmanagedType.I1)]
- static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename);
-
- public TLSConfiguration() {
- self = TLSConfiguration_create ();
- }
-
- ~TLSConfiguration()
- {
- Dispose ();
- }
-
- internal IntPtr GetNativeInstance()
- {
- return self;
- }
-
- public bool AllowOnlyKnownCertificates
- {
- set {
- TLSConfiguration_setAllowOnlyKnownCertificates (self, value);
- allowOnlyKnownCerts = value;
- }
- get {
- return allowOnlyKnownCerts;
- }
- }
-
- public bool ChainValidation
- {
- set {
- TLSConfiguration_setChainValidation (self, value);
- chainValidation = value;
- }
- get {
- return chainValidation;
- }
- }
-
-
- public void SetClientMode()
- {
- TLSConfiguration_setClientMode (self);
- }
-
- public void SetOwnCertificate(string filename)
- {
- if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) {
- throw new CryptographicException ("Failed to read certificate from file");
- }
- }
-
- public void SetOwnCertificate(X509Certificate2 cert)
- {
- byte[] certBytes = cert.GetRawCertData ();
-
- if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) {
- throw new CryptographicException ("Failed to set certificate");
- }
- }
-
- public void AddAllowedCertificate(string filename)
- {
- if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) {
- throw new CryptographicException ("Failed to read allowed certificate from file");
- }
- }
-
- public void AddAllowedCertificate(X509Certificate2 cert)
- {
- byte[] certBytes = cert.GetRawCertData ();
-
- if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) {
- throw new CryptographicException ("Failed to add allowed certificate");
- }
- }
-
- public void AddCACertificate(string filename)
- {
- if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) {
- throw new CryptographicException ("Failed to read CA certificate from file");
- }
- }
-
- public void AddCACertificate(X509Certificate2 cert)
- {
- byte[] certBytes = cert.GetRawCertData ();
-
- if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) {
- throw new CryptographicException ("Failed to add CA certificate");
- }
- }
-
- public void SetOwnKey (string filename, string password)
- {
- if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) {
- throw new CryptographicException ("Failed to read own key from file");
- }
- }
-
- public void SetOwnKey (X509Certificate2 key, string password)
- {
- byte[] certBytes = key.Export (X509ContentType.Pkcs12);
-
- if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) {
- throw new CryptographicException ("Failed to set own key");
- }
- }
-
- public void Dispose()
- {
- lock (this) {
- if (self != IntPtr.Zero) {
- TLSConfiguration_destroy (self);
- self = IntPtr.Zero;
- }
- }
- }
-
- }
- }
-}
\ No newline at end of file
+ namespace TLS
+ {
+ public enum TLSConfigVersion
+ {
+ NOT_SELECTED = 0,
+ SSL_3_0 = 3,
+ TLS_1_0 = 4,
+ TLS_1_1 = 5,
+ TLS_1_2 = 6,
+ TLS_1_3 = 7
+ }
+
+ public enum TLSEventLevel
+ {
+ INFO = 0,
+ WARNING = 1,
+ INCIDENT = 2
+ }
+
+ public enum TLSEventCode
+ {
+ ALM_ALGO_NOT_SUPPORTED = 1,
+ ALM_UNSECURE_COMMUNICATION = 2,
+ ALM_CERT_UNAVAILABLE = 3,
+ ALM_BAD_CERT = 4,
+ ALM_CERT_SIZE_EXCEEDED = 5,
+ ALM_CERT_VALIDATION_FAILED = 6,
+ ALM_CERT_REQUIRED = 7,
+ ALM_HANDSHAKE_FAILED_UNKNOWN_REASON = 8,
+ WRN_INSECURE_TLS_VERSION = 9,
+ INF_SESSION_RENEGOTIATION = 10,
+ ALM_CERT_EXPIRED = 11,
+ ALM_CERT_REVOKED = 12,
+ ALM_CERT_NOT_CONFIGURED = 13,
+ ALM_CERT_NOT_TRUSTED = 14
+ }
+
+ public class TLSConnection
+ {
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern int TLSConnection_getTLSVersion(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr TLSConnection_getPeerAddress(IntPtr self, IntPtr peerAddrBuf);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr TLSConnection_getPeerCertificate(IntPtr self, out int certSize);
+
+ private IntPtr self;
+ private bool isValid;
+
+ internal TLSConnection(IntPtr self)
+ {
+ this.self = self;
+ isValid = true;
+ }
+
+ // To be called by event callback caller after callback execution
+ internal void InValidate()
+ {
+ lock (this)
+ {
+ isValid = false;
+ }
+ }
+
+ ///
+ /// TLS version used by the connection
+ ///
+ public TLSConfigVersion TLSVersion
+ {
+ get
+ {
+ lock (this)
+ {
+ if (isValid)
+ {
+ return (TLSConfigVersion)TLSConnection_getTLSVersion((IntPtr)self);
+ }
+ else
+ {
+ throw new InvalidOperationException("Object cannot be used outside of TLS event callback");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Peer IP address and TCP port of the TLS connection
+ ///
+ public string PeerAddress
+ {
+ get
+ {
+ lock (this)
+ {
+ if (isValid)
+ {
+ IntPtr peerAddrBuf = Marshal.AllocHGlobal(130);
+ IntPtr peerAddrStr = TLSConnection_getPeerAddress(this.self, peerAddrBuf);
+
+ string peerAddr = null;
+
+ if (peerAddrStr != IntPtr.Zero)
+ {
+ peerAddr = Marshal.PtrToStringAnsi(peerAddrStr);
+ }
+
+ Marshal.FreeHGlobal(peerAddrBuf);
+
+ return peerAddr;
+ }
+ else
+ {
+ throw new InvalidOperationException("Object cannot be used outside of TLS event callback");
+ }
+ }
+ }
+ }
+
+ ///
+ /// TLS certificate used by the peer
+ ///
+ public byte[] PeerCertificate
+ {
+ get
+ {
+ lock (this)
+ {
+ if (isValid)
+ {
+ int certSize;
+
+ IntPtr certBuffer = TLSConnection_getPeerCertificate(self, out certSize);
+
+ if (certBuffer != IntPtr.Zero)
+ {
+ if (certSize > 0)
+ {
+ byte[] cert = new byte[certSize];
+
+ Marshal.Copy(certBuffer, cert, 0, certSize);
+
+ return cert;
+ }
+ }
+
+ return null;
+ }
+ else
+ {
+ throw new InvalidOperationException("Object cannot be used outside of TLS event callback");
+ }
+ }
+ }
+ }
+
+ }
+
+ ///
+ /// TLS security event handler
+ ///
+ /// user provided context paramter to be passed to the handler
+ /// severity level of the event
+ /// code to identify the event type
+ /// text message describing the event
+ /// TLS connection that caused the event
+ public delegate void TLSEventHandler(object parameter, TLSEventLevel eventLevel, TLSEventCode eventCode, string message, TLSConnection connection);
+
+ ///
+ /// A container for TLS configuration and certificates.
+ ///
+ public class TLSConfiguration : IDisposable
+ {
+ private IntPtr self = IntPtr.Zero;
+
+ private bool allowOnlyKnownCerts = false;
+ private bool chainValidation = true;
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern IntPtr TLSConfiguration_create();
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void TLSConfiguration_destroy(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void TLSConfiguration_setChainValidation(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void TLSConfiguration_setClientMode(IntPtr self);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_setOwnKeyFromFile(IntPtr self, string filename, string keyPassword);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen);
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ [return: MarshalAs(UnmanagedType.I1)]
+ static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename);
+
+ private TLSEventHandler eventHandler = null;
+ private object eventHandlerParameter = null;
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ private delegate void InternalTLSEventHandler(IntPtr parameter, int eventLevel, int eventCode, IntPtr message, IntPtr tlsCon);
+
+ private InternalTLSEventHandler internalTLSEventHandlerRef = null;
+
+ [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
+ static extern void TLSConfiguration_setEventHandler(IntPtr self, InternalTLSEventHandler handler, IntPtr parameter);
+
+ void InternalTLSEventHandlerImpl(IntPtr parameter, int eventLevel, int eventCode, IntPtr message, IntPtr tlsCon)
+ {
+ if (eventHandler != null)
+ {
+ TLSConnection connection = new TLSConnection(tlsCon);
+
+ string msg = Marshal.PtrToStringAnsi(message);
+
+ eventHandler(eventHandlerParameter, (TLSEventLevel)eventLevel, (TLSEventCode)eventCode, msg, connection);
+
+ connection.InValidate();
+ }
+ }
+
+ public void SetEventHandler(TLSEventHandler handler, object parameter)
+ {
+ this.eventHandler = handler;
+ this.eventHandlerParameter = parameter;
+
+ if (internalTLSEventHandlerRef == null)
+ {
+ internalTLSEventHandlerRef = new InternalTLSEventHandler(InternalTLSEventHandlerImpl);
+
+ TLSConfiguration_setEventHandler(self, internalTLSEventHandlerRef, IntPtr.Zero);
+ }
+ }
+
+ public TLSConfiguration()
+ {
+ self = TLSConfiguration_create();
+ }
+
+ ~TLSConfiguration()
+ {
+ Dispose();
+ }
+
+ internal IntPtr GetNativeInstance()
+ {
+ return self;
+ }
+
+ public bool AllowOnlyKnownCertificates
+ {
+ set
+ {
+ TLSConfiguration_setAllowOnlyKnownCertificates(self, value);
+ allowOnlyKnownCerts = value;
+ }
+ get
+ {
+ return allowOnlyKnownCerts;
+ }
+ }
+
+ public bool ChainValidation
+ {
+ set
+ {
+ TLSConfiguration_setChainValidation(self, value);
+ chainValidation = value;
+ }
+ get
+ {
+ return chainValidation;
+ }
+ }
+
+ public void SetClientMode()
+ {
+ TLSConfiguration_setClientMode(self);
+ }
+
+ public void SetOwnCertificate(string filename)
+ {
+ if (TLSConfiguration_setOwnCertificateFromFile(self, filename) == false)
+ {
+ throw new CryptographicException("Failed to read certificate from file");
+ }
+ }
+
+ public void SetOwnCertificate(X509Certificate2 cert)
+ {
+ byte[] certBytes = cert.GetRawCertData();
+
+ if (TLSConfiguration_setOwnCertificate(self, certBytes, certBytes.Length) == false)
+ {
+ throw new CryptographicException("Failed to set certificate");
+ }
+ }
+
+ public void AddAllowedCertificate(string filename)
+ {
+ if (TLSConfiguration_addAllowedCertificateFromFile(self, filename) == false)
+ {
+ throw new CryptographicException("Failed to read allowed certificate from file");
+ }
+ }
+
+ public void AddAllowedCertificate(X509Certificate2 cert)
+ {
+ byte[] certBytes = cert.GetRawCertData();
+
+ if (TLSConfiguration_addAllowedCertificate(self, certBytes, certBytes.Length) == false)
+ {
+ throw new CryptographicException("Failed to add allowed certificate");
+ }
+ }
+
+ public void AddCACertificate(string filename)
+ {
+ if (TLSConfiguration_addCACertificateFromFile(self, filename) == false)
+ {
+ throw new CryptographicException("Failed to read CA certificate from file");
+ }
+ }
+
+ public void AddCACertificate(X509Certificate2 cert)
+ {
+ byte[] certBytes = cert.GetRawCertData();
+
+ if (TLSConfiguration_addCACertificate(self, certBytes, certBytes.Length) == false)
+ {
+ throw new CryptographicException("Failed to add CA certificate");
+ }
+ }
+
+ public void SetOwnKey(string filename, string password)
+ {
+ if (TLSConfiguration_setOwnKeyFromFile(self, filename, password) == false)
+ {
+ throw new CryptographicException("Failed to read own key from file");
+ }
+ }
+
+ public void SetOwnKey(X509Certificate2 key, string password)
+ {
+ byte[] certBytes = key.Export(X509ContentType.Pkcs12);
+
+ if (TLSConfiguration_setOwnKey(self, certBytes, certBytes.Length, password) == false)
+ {
+ throw new CryptographicException("Failed to set own key");
+ }
+ }
+
+ public void Dispose()
+ {
+ lock (this)
+ {
+ if (self != IntPtr.Zero)
+ {
+ TLSConfiguration_destroy(self);
+ self = IntPtr.Zero;
+ }
+ }
+ }
+
+ }
+ }
+}
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index ac08165a..f551ec6e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -14,6 +14,7 @@ add_subdirectory(server_example_files)
add_subdirectory(server_example_substitution)
add_subdirectory(server_example_service_tracking)
add_subdirectory(server_example_deadband)
+add_subdirectory(server_example_access_control)
add_subdirectory(iec61850_client_example1)
add_subdirectory(iec61850_client_example2)
diff --git a/examples/iec61850_client_example1/client_example1.c b/examples/iec61850_client_example1/client_example1.c
index f84d37a0..6c9241f0 100644
--- a/examples/iec61850_client_example1/client_example1.c
+++ b/examples/iec61850_client_example1/client_example1.c
@@ -33,6 +33,9 @@ int main(int argc, char** argv) {
char* hostname;
int tcpPort = 102;
+ const char* localIp = NULL;
+ int localTcpPort = -1;
+
if (argc > 1)
hostname = argv[1];
@@ -42,19 +45,34 @@ int main(int argc, char** argv) {
if (argc > 2)
tcpPort = atoi(argv[2]);
+ if (argc > 3)
+ localIp = argv[3];
+
+ if (argc > 4)
+ localTcpPort = atoi(argv[4]);
+
IedClientError error;
IedConnection con = IedConnection_create();
+ /* Optional bind to local IP address/interface */
+ if (localIp) {
+ IedConnection_setLocalAddress(con, localIp, localTcpPort);
+ printf("Bound to Local Address: %s:%i\n", localIp, localTcpPort);
+ }
+
IedConnection_connect(con, &error, hostname, tcpPort);
+ printf("Connecting to %s:%i\n", hostname, tcpPort);
- if (error == IED_ERROR_OK) {
+ if (error == IED_ERROR_OK)
+ {
+ printf("Connected\n");
/* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX);
- if (value != NULL) {
-
+ if (value != NULL)
+ {
if (MmsValue_getType(value) == MMS_FLOAT) {
float fval = MmsValue_toFloat(value);
printf("read float value: %f\n", fval);
@@ -137,7 +155,6 @@ close_connection:
}
IedConnection_destroy(con);
+
return 0;
}
-
-
diff --git a/examples/iec61850_client_example_control/client_example_control.c b/examples/iec61850_client_example_control/client_example_control.c
index 33da635a..37d6b6bc 100644
--- a/examples/iec61850_client_example_control/client_example_control.c
+++ b/examples/iec61850_client_example_control/client_example_control.c
@@ -45,8 +45,10 @@ int main(int argc, char** argv) {
IedConnection_connect(con, &error, hostname, tcpPort);
- if (error == IED_ERROR_OK) {
-
+ if (error == IED_ERROR_OK)
+ {
+ MmsValue* ctlVal = NULL;
+ MmsValue* stVal = NULL;
/************************
* Direct control
@@ -55,99 +57,116 @@ int main(int argc, char** argv) {
ControlObjectClient control
= ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con);
- MmsValue* ctlVal = MmsValue_newBoolean(true);
+ if (control)
+ {
+ ctlVal = MmsValue_newBoolean(true);
- ControlObjectClient_setOrigin(control, NULL, 3);
+ ControlObjectClient_setOrigin(control, NULL, 3);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
- }
- else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n");
- }
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n");
+ }
+
+ MmsValue_delete(ctlVal);
- MmsValue_delete(ctlVal);
+ ControlObjectClient_destroy(control);
- ControlObjectClient_destroy(control);
+ /* Check if status value has changed */
- /* Check if status value has changed */
+ stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST);
- MmsValue* stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST);
+ if (error == IED_ERROR_OK) {
+ bool state = MmsValue_getBoolean(stVal);
+ MmsValue_delete(stVal);
- if (error == IED_ERROR_OK) {
- bool state = MmsValue_getBoolean(stVal);
- MmsValue_delete(stVal);
+ printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
+ }
+ else {
+ printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
+ }
- printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
}
else {
- printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO1 not found in server\n");
}
-
/************************
* Select before operate
***********************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO2", con);
- if (ControlObjectClient_select(control)) {
+ if (control)
+ {
+ if (ControlObjectClient_select(control)) {
- ctlVal = MmsValue_newBoolean(true);
+ ctlVal = MmsValue_newBoolean(true);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
+ }
+
+ MmsValue_delete(ctlVal);
}
else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
+ printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
}
- MmsValue_delete(ctlVal);
+ ControlObjectClient_destroy(control);
}
else {
- printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO2 not found in server\n");
}
- ControlObjectClient_destroy(control);
-
-
/****************************************
* Direct control with enhanced security
****************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con);
- ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
+ if (control)
+ {
+ ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
- ctlVal = MmsValue_newBoolean(true);
+ ctlVal = MmsValue_newBoolean(true);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
- }
- else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
- }
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
+ }
- MmsValue_delete(ctlVal);
+ MmsValue_delete(ctlVal);
- /* Wait for command termination message */
- Thread_sleep(1000);
+ /* Wait for command termination message */
+ Thread_sleep(1000);
- ControlObjectClient_destroy(control);
+ ControlObjectClient_destroy(control);
- /* Check if status value has changed */
+ /* Check if status value has changed */
- stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST);
+ stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST);
- if (error == IED_ERROR_OK) {
- bool state = MmsValue_getBoolean(stVal);
+ if (error == IED_ERROR_OK) {
+ bool state = MmsValue_getBoolean(stVal);
- printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
+ printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state);
- MmsValue_delete(stVal);
+ MmsValue_delete(stVal);
+ }
+ else {
+ printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
+ }
}
else {
- printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO3 not found in server\n");
}
/***********************************************
@@ -156,56 +175,66 @@ int main(int argc, char** argv) {
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO4", con);
- ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
+ if (control)
+ {
+ ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
- ctlVal = MmsValue_newBoolean(true);
+ ctlVal = MmsValue_newBoolean(true);
- if (ControlObjectClient_selectWithValue(control, ctlVal)) {
+ if (ControlObjectClient_selectWithValue(control, ctlVal)) {
+
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
+ }
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n");
}
else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n");
+ printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
}
+ MmsValue_delete(ctlVal);
+
+ /* Wait for command termination message */
+ Thread_sleep(1000);
+
+ ControlObjectClient_destroy(control);
}
else {
- printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n");
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO4 not found in server\n");
}
- MmsValue_delete(ctlVal);
-
- /* Wait for command termination message */
- Thread_sleep(1000);
-
- ControlObjectClient_destroy(control);
-
-
/*********************************************************************
* Direct control with enhanced security (expect CommandTermination-)
*********************************************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con);
- ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
-
- ctlVal = MmsValue_newBoolean(true);
+ if (control)
+ {
+ ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
- if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
- printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
- }
- else {
- printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
- }
+ ctlVal = MmsValue_newBoolean(true);
- MmsValue_delete(ctlVal);
+ if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
+ printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
+ }
+ else {
+ printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
+ }
- /* Wait for command termination message */
- Thread_sleep(1000);
+ MmsValue_delete(ctlVal);
- ControlObjectClient_destroy(control);
+ /* Wait for command termination message */
+ Thread_sleep(1000);
+ ControlObjectClient_destroy(control);
+ }
+ else {
+ printf("Control object simpleIOGenericIO/GGIO1.SPCSO9 not found in server\n");
+ }
IedConnection_close(con);
}
diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c
index 8b43d9d7..24c909c9 100644
--- a/examples/mms_utility/mms_utility.c
+++ b/examples/mms_utility/mms_utility.c
@@ -101,6 +101,7 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re
int main(int argc, char** argv)
{
+ int returnCode = 0;
char* hostname = StringUtils_copyString("localhost");
int tcpPort = 102;
@@ -213,6 +214,10 @@ int main(int argc, char** argv)
if (!MmsConnection_connect(con, &error, hostname, tcpPort)) {
printf("MMS connect failed!\n");
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
goto exit;
}
else
@@ -222,6 +227,9 @@ int main(int argc, char** argv)
MmsServerIdentity* identity =
MmsConnection_identify(con, &error);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
if (identity != NULL) {
printf("\nServer identity:\n----------------\n");
printf(" vendor:\t%s\n", identity->vendorName);
@@ -235,14 +243,23 @@ int main(int argc, char** argv)
if (readDeviceList) {
printf("\nDomains present on server:\n--------------------------\n");
LinkedList nameList = MmsConnection_getDomainNames(con, &error);
- LinkedList_printStringList(nameList);
- LinkedList_destroy(nameList);
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
+ if (nameList) {
+ LinkedList_printStringList(nameList);
+ LinkedList_destroy(nameList);
+ }
}
if (getDeviceDirectory) {
LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error,
domainName);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
if (variableList) {
LinkedList element = LinkedList_getNext(variableList);
@@ -264,6 +281,9 @@ int main(int argc, char** argv)
variableList = MmsConnection_getDomainJournals(con, &error, domainName);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
if (variableList) {
LinkedList element = variableList;
@@ -309,6 +329,9 @@ int main(int argc, char** argv)
LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime,
&moreFollows);
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
MmsValue_delete(startTime);
MmsValue_delete(endTime);
@@ -375,6 +398,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
+
+ returnCode = error;
}
else {
printf("Read SUCCESS\n");
@@ -403,6 +428,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error);
+
+ returnCode = error;
}
else {
printf("Read SUCCESS\n");
@@ -421,6 +448,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Reading variable list directory failed: (ERROR %i)\n", error);
+
+ returnCode = error;
}
else {
LinkedList varListElem = LinkedList_getNext(varListDir);
@@ -454,12 +483,19 @@ int main(int argc, char** argv)
char* continueAfter = NULL;
while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) {
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
+
continueAfter = lastName;
}
}
if (getFileAttributes) {
MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL);
+
+ if (error != MMS_ERROR_NONE)
+ returnCode = error;
}
if (deleteFile) {
@@ -467,13 +503,14 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) {
printf("Delete file failed: (ERROR %i)\n", error);
+ returnCode = error;
}
else {
printf("File deleted\n");
}
}
- exit:
+exit:
free(hostname);
free(domainName);
free(variableName);
@@ -482,6 +519,6 @@ int main(int argc, char** argv)
MmsConnection_destroy(con);
- return 0;
+ return returnCode;
}
diff --git a/examples/r_goose_publisher_example/r_goose_publisher_example.c b/examples/r_goose_publisher_example/r_goose_publisher_example.c
index 0434c343..9ec300bb 100644
--- a/examples/r_goose_publisher_example/r_goose_publisher_example.c
+++ b/examples/r_goose_publisher_example/r_goose_publisher_example.c
@@ -102,7 +102,3 @@ main(int argc, char **argv)
return 0;
}
-
-
-
-
diff --git a/examples/server_example_access_control/CMakeLists.txt b/examples/server_example_access_control/CMakeLists.txt
new file mode 100644
index 00000000..a71a7383
--- /dev/null
+++ b/examples/server_example_access_control/CMakeLists.txt
@@ -0,0 +1,21 @@
+include_directories(
+ .
+)
+
+set(server_example_SRCS
+ server_example_access_control.c
+ static_model.c
+)
+
+IF(MSVC)
+set_source_files_properties(${server_example_SRCS}
+ PROPERTIES LANGUAGE CXX)
+ENDIF(MSVC)
+
+add_executable(server_example_access_control
+ ${server_example_SRCS}
+)
+
+target_link_libraries(server_example_access_control
+ iec61850
+)
diff --git a/examples/server_example_access_control/Makefile b/examples/server_example_access_control/Makefile
new file mode 100644
index 00000000..4b288a51
--- /dev/null
+++ b/examples/server_example_access_control/Makefile
@@ -0,0 +1,32 @@
+LIBIEC_HOME=../..
+
+PROJECT_BINARY_NAME = server_example_access_control
+PROJECT_SOURCES = server_example_access_control.c
+PROJECT_SOURCES += static_model.c
+
+PROJECT_ICD_FILE = simpleIO_direct_control.cid
+
+include $(LIBIEC_HOME)/make/target_system.mk
+include $(LIBIEC_HOME)/make/stack_includes.mk
+
+all: $(PROJECT_BINARY_NAME)
+
+include $(LIBIEC_HOME)/make/common_targets.mk
+
+LDLIBS += -lm
+
+CP = cp
+
+model: $(PROJECT_ICD_FILE)
+ java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE)
+
+$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS)
+ mkdir -p vmd-filestore
+ $(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN
+
+clean:
+ rm -f $(PROJECT_BINARY_NAME)
+ rm -f vmd-filestore/IEDSERVER.BIN
+
+
diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c
new file mode 100644
index 00000000..e0b789af
--- /dev/null
+++ b/examples/server_example_access_control/server_example_access_control.c
@@ -0,0 +1,324 @@
+/*
+ * server_example_access_control.c
+ *
+ * - How to use access control mechanisms
+ * - How to implement RBAC features based on access control mechanisms
+ */
+
+#include "iec61850_server.h"
+#include "hal_thread.h"
+#include
+#include
+#include
+#include
+
+#include "static_model.h"
+
+static int running = 0;
+static IedServer iedServer = NULL;
+
+void
+sigint_handler(int signalId)
+{
+ running = 0;
+}
+
+static ControlHandlerResult
+controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test)
+{
+ if (test)
+ return CONTROL_RESULT_FAILED;
+
+ if (MmsValue_getType(value) == MMS_BOOLEAN) {
+ printf("received binary control command: ");
+
+ if (MmsValue_getBoolean(value))
+ printf("on\n");
+ else
+ printf("off\n");
+ }
+ else
+ return CONTROL_RESULT_FAILED;
+
+ uint64_t timeStamp = Hal_getTimeInMs();
+
+ if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) {
+ IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp);
+ IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value);
+ }
+
+ if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2) {
+ IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp);
+ IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value);
+ }
+
+ if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) {
+ IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp);
+ IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value);
+ }
+
+ if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4) {
+ IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, timeStamp);
+ IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value);
+ }
+
+ return CONTROL_RESULT_OK;
+}
+
+
+static void
+connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter)
+{
+ if (connected)
+ printf("Connection opened\n");
+ else
+ printf("Connection closed\n");
+}
+
+/*
+ * This handler is called before the rcbEventHandler and can be use to allow or permit read or write access to the RCB
+ */
+static bool
+rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation)
+{
+ printf("RCB: %s access: %s\n", ReportControlBlock_getName(rcb), operation == RCB_EVENT_GET_PARAMETER ? "READ" : "WRITE");
+
+ if (operation == RCB_EVENT_GET_PARAMETER) {
+ return true;
+ }
+ else {
+ /* change to false to disallow write access to control block */
+ return true;
+ }
+}
+
+static bool
+lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation)
+{
+ printf("LCB: %s access: %s\n", LogControlBlock_getName(lcb), operation == LCB_EVENT_GET_PARAMETER ? "READ" : "WRITE");
+
+ if (operation == LCB_EVENT_GET_PARAMETER) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+static void
+rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError)
+{
+ printf("RCB: %s event: %i\n", ReportControlBlock_getName(rcb), event);
+
+ if ((event == RCB_EVENT_SET_PARAMETER) || (event == RCB_EVENT_GET_PARAMETER)) {
+ printf(" param: %s\n", parameterName);
+ printf(" result: %i\n", serviceError);
+ }
+
+ if (event == RCB_EVENT_ENABLE) {
+ char* rptId = ReportControlBlock_getRptID(rcb);
+ printf(" rptID: %s\n", rptId);
+ char* dataSet = ReportControlBlock_getDataSet(rcb);
+ printf(" datSet: %s\n", dataSet);
+
+ free(rptId);
+ free(dataSet);
+ }
+}
+
+static bool
+dataSetAccessHandler(void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef)
+{
+ printf("Data set access: %s operation: %i\n", datasetRef, operation);
+
+ return true;
+}
+
+static MmsDataAccessError
+readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc, ClientConnection connection, void* parameter)
+{
+ printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name);
+
+ if (!strcmp(ln->name, "GGIO1") && !strcmp(dataObject->name, "AnIn1")) {
+ return DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ }
+
+ return DATA_ACCESS_ERROR_SUCCESS;
+}
+
+static bool
+directoryAccessHandler(void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice)
+{
+ switch(category) {
+ case DIRECTORY_CAT_LD_LIST:
+ printf("Get list of logical devices from %s\n", ClientConnection_getPeerAddress(connection));
+ break;
+ case DIRECTORY_CAT_DATASET_LIST:
+ printf("Get list of datasets for LD %s from %s\n", ModelNode_getName((ModelNode*)logicalDevice), ClientConnection_getPeerAddress(connection));
+ break;
+ case DIRECTORY_CAT_DATA_LIST:
+ printf("Get list of data for LD %s from %s\n", ModelNode_getName((ModelNode*)logicalDevice), ClientConnection_getPeerAddress(connection));
+ break;
+ case DIRECTORY_CAT_LOG_LIST:
+ printf("Get list of logs for LD %s from %s -> reject\n", ModelNode_getName((ModelNode*)logicalDevice), ClientConnection_getPeerAddress(connection));
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+int
+main(int argc, char** argv)
+{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
+ printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
+
+ /* Create new server configuration object */
+ IedServerConfig config = IedServerConfig_create();
+
+ /* Set buffer size for buffered report control blocks to 200000 bytes */
+ IedServerConfig_setReportBufferSize(config, 200000);
+
+ /* Set stack compliance to a specific edition of the standard (WARNING: data model has also to be checked for compliance) */
+ IedServerConfig_setEdition(config, IEC_61850_EDITION_2);
+
+ /* Set the base path for the MMS file services */
+ IedServerConfig_setFileServiceBasePath(config, "./vmd-filestore/");
+
+ /* disable MMS file service */
+ IedServerConfig_enableFileService(config, false);
+
+ /* enable dynamic data set service */
+ IedServerConfig_enableDynamicDataSetService(config, true);
+
+ /* disable log service */
+ IedServerConfig_enableLogService(config, false);
+
+ /* set maximum number of clients */
+ IedServerConfig_setMaxMmsConnections(config, 2);
+
+ /* Create a new IEC 61850 server instance */
+ iedServer = IedServer_createWithConfig(&iedModel, NULL, config);
+
+ /* configuration object is no longer required */
+ IedServerConfig_destroy(config);
+
+ /* set the identity values for MMS identify service */
+ IedServer_setServerIdentity(iedServer, "libiec61850.com", "access control example", "1.0.0");
+
+ /* Install handler for operate command */
+ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,
+ (ControlHandler) controlHandlerForBinaryOutput,
+ IEDMODEL_GenericIO_GGIO1_SPCSO1);
+
+ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2,
+ (ControlHandler) controlHandlerForBinaryOutput,
+ IEDMODEL_GenericIO_GGIO1_SPCSO2);
+
+ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3,
+ (ControlHandler) controlHandlerForBinaryOutput,
+ IEDMODEL_GenericIO_GGIO1_SPCSO3);
+
+ IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4,
+ (ControlHandler) controlHandlerForBinaryOutput,
+ IEDMODEL_GenericIO_GGIO1_SPCSO4);
+
+ IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
+
+ /* Install handler to perform access control on RCB */
+ IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL);
+
+ /* Install handler to perform access control on LCB */
+ IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL);
+
+ /* Install handler to log RCB events */
+ IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL);
+
+ /* By default access to variables with FC=DC and FC=CF is not allowed.
+ * This allow to write to simpleIOGenericIO/GGIO1.NamPlt.vendor variable used
+ * by iec61850_client_example1.
+ */
+ IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);
+
+ /* Install handler to perform access control on datasets */
+ IedServer_setDataSetAccessHandler(iedServer, dataSetAccessHandler, NULL);
+
+ /* Install handler to perform read access control on data model elements
+ * NOTE: when read access to a data model element is blocked this will also prevent the client
+ * to read the data model element in a data set or enable a RCB instance that uses a dataset
+ * containing the restricted data model element.
+ */
+ IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL);
+
+ IedServer_setDirectoryAccessHandler(iedServer, directoryAccessHandler, NULL);
+
+ /* MMS server will be instructed to start listening for client connections. */
+ IedServer_start(iedServer, tcpPort);
+
+ if (!IedServer_isRunning(iedServer)) {
+ printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n");
+ IedServer_destroy(iedServer);
+ exit(-1);
+ }
+
+ running = 1;
+
+ signal(SIGINT, sigint_handler);
+
+ float t = 0.f;
+
+ while (running) {
+ uint64_t timestamp = Hal_getTimeInMs();
+
+ t += 0.1f;
+
+ float an1 = sinf(t);
+ float an2 = sinf(t + 1.f);
+ float an3 = sinf(t + 2.f);
+ float an4 = sinf(t + 3.f);
+
+ Timestamp iecTimestamp;
+
+ Timestamp_clearFlags(&iecTimestamp);
+ Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp);
+ Timestamp_setLeapSecondKnown(&iecTimestamp, true);
+
+ /* toggle clock-not-synchronized flag in timestamp */
+ if (((int) t % 2) == 0)
+ Timestamp_setClockNotSynchronized(&iecTimestamp, true);
+
+#if 1
+ IedServer_lockDataModel(iedServer);
+
+ IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp);
+ IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1);
+
+ IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, &iecTimestamp);
+ IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, an2);
+
+ IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, &iecTimestamp);
+ IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, an3);
+
+ IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, &iecTimestamp);
+ IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, an4);
+
+ IedServer_unlockDataModel(iedServer);
+#endif
+
+ Thread_sleep(100);
+ }
+
+ /* stop MMS server - close TCP server socket and all client sockets */
+ IedServer_stop(iedServer);
+
+ /* Cleanup - free all resources */
+ IedServer_destroy(iedServer);
+
+ return 0;
+} /* main() */
diff --git a/examples/server_example_access_control/static_model.c b/examples/server_example_access_control/static_model.c
new file mode 100644
index 00000000..26f5f4fc
--- /dev/null
+++ b/examples/server_example_access_control/static_model.c
@@ -0,0 +1,2223 @@
+/*
+ * static_model.c
+ *
+ * automatically generated from simpleIO_direct_control.cid
+ */
+#include "static_model.h"
+
+static void initializeValues();
+
+extern DataSet iedModelds_GenericIO_LLN0_Events;
+extern DataSet iedModelds_GenericIO_LLN0_Events2;
+extern DataSet iedModelds_GenericIO_LLN0_Measurements;
+
+
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3;
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO1$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events_fcda1
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO2$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events_fcda2
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO3$stVal",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events_fcda3
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO4$stVal",
+ -1,
+ NULL,
+ NULL,
+ NULL
+};
+
+DataSet iedModelds_GenericIO_LLN0_Events = {
+ "GenericIO",
+ "LLN0$Events",
+ 4,
+ &iedModelds_GenericIO_LLN0_Events_fcda0,
+ &iedModelds_GenericIO_LLN0_Events2
+};
+
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3;
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO1",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events2_fcda1
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO2",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events2_fcda2
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO3",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Events2_fcda3
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3 = {
+ "GenericIO",
+ false,
+ "GGIO1$ST$SPCSO4",
+ -1,
+ NULL,
+ NULL,
+ NULL
+};
+
+DataSet iedModelds_GenericIO_LLN0_Events2 = {
+ "GenericIO",
+ "LLN0$Events2",
+ 4,
+ &iedModelds_GenericIO_LLN0_Events2_fcda0,
+ &iedModelds_GenericIO_LLN0_Measurements
+};
+
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6;
+extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7;
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn1$mag$f",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda1
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn1$q",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda2
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn2$mag$f",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda3
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn2$q",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda4
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn3$mag$f",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda5
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn3$q",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda6
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn4$mag$f",
+ -1,
+ NULL,
+ NULL,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda7
+};
+
+DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7 = {
+ "GenericIO",
+ false,
+ "GGIO1$MX$AnIn4$q",
+ -1,
+ NULL,
+ NULL,
+ NULL
+};
+
+DataSet iedModelds_GenericIO_LLN0_Measurements = {
+ "GenericIO",
+ "LLN0$Measurements",
+ 8,
+ &iedModelds_GenericIO_LLN0_Measurements_fcda0,
+ NULL
+};
+
+LogicalDevice iedModel_GenericIO = {
+ LogicalDeviceModelType,
+ "GenericIO",
+ (ModelNode*) &iedModel,
+ NULL,
+ (ModelNode*) &iedModel_GenericIO_LLN0,
+ NULL
+};
+
+LogicalNode iedModel_GenericIO_LLN0 = {
+ LogicalNodeModelType,
+ "LLN0",
+ (ModelNode*) &iedModel_GenericIO,
+ (ModelNode*) &iedModel_GenericIO_LPHD1,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod,
+};
+
+DataObject iedModel_GenericIO_LLN0_Mod = {
+ DataObjectModelType,
+ "Mod",
+ (ModelNode*) &iedModel_GenericIO_LLN0,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Mod_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Mod_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod_ctlModel,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Mod,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_LLN0_Beh = {
+ DataObjectModelType,
+ "Beh",
+ (ModelNode*) &iedModel_GenericIO_LLN0,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Beh_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Beh_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Beh,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_LLN0_Health = {
+ DataObjectModelType,
+ "Health",
+ (ModelNode*) &iedModel_GenericIO_LLN0,
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LLN0_Health_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Health_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health,
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_Health_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_LLN0_Health,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_LLN0_NamPlt = {
+ DataObjectModelType,
+ "NamPlt",
+ (ModelNode*) &iedModel_GenericIO_LLN0,
+ NULL,
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_vendor,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor = {
+ DataAttributeModelType,
+ "vendor",
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_swRev,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev = {
+ DataAttributeModelType,
+ "swRev",
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_d,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_NamPlt_d = {
+ DataAttributeModelType,
+ "d",
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_configRev,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev = {
+ DataAttributeModelType,
+ "configRev",
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_ldNs,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs = {
+ DataAttributeModelType,
+ "ldNs",
+ (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_EX,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+LogicalNode iedModel_GenericIO_LPHD1 = {
+ LogicalNodeModelType,
+ "LPHD1",
+ (ModelNode*) &iedModel_GenericIO,
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam,
+};
+
+DataObject iedModel_GenericIO_LPHD1_PhyNam = {
+ DataObjectModelType,
+ "PhyNam",
+ (ModelNode*) &iedModel_GenericIO_LPHD1,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam_vendor,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor = {
+ DataAttributeModelType,
+ "vendor",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_LPHD1_PhyHealth = {
+ DataObjectModelType,
+ "PhyHealth",
+ (ModelNode*) &iedModel_GenericIO_LPHD1,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_LPHD1_Proxy = {
+ DataObjectModelType,
+ "Proxy",
+ (ModelNode*) &iedModel_GenericIO_LPHD1,
+ NULL,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LPHD1_Proxy_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy,
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_LPHD1_Proxy_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+LogicalNode iedModel_GenericIO_GGIO1 = {
+ LogicalNodeModelType,
+ "GGIO1",
+ (ModelNode*) &iedModel_GenericIO,
+ NULL,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod,
+};
+
+DataObject iedModel_GenericIO_GGIO1_Mod = {
+ DataObjectModelType,
+ "Mod",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Mod_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Mod_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_ctlModel,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Mod,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_Beh = {
+ DataObjectModelType,
+ "Beh",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Beh_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Beh_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Beh,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_Health = {
+ DataObjectModelType,
+ "Health",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Health_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Health_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Health,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_NamPlt = {
+ DataObjectModelType,
+ "NamPlt",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_vendor,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor = {
+ DataAttributeModelType,
+ "vendor",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_swRev,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev = {
+ DataAttributeModelType,
+ "swRev",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_d,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d = {
+ DataAttributeModelType,
+ "d",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_DC,
+ IEC61850_VISIBLE_STRING_255,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_AnIn1 = {
+ DataObjectModelType,
+ "AnIn1",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_q,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag_f,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_AnIn2 = {
+ DataObjectModelType,
+ "AnIn2",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_q,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag_f,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_AnIn3 = {
+ DataObjectModelType,
+ "AnIn3",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_q,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag_f,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_AnIn4 = {
+ DataObjectModelType,
+ "AnIn4",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag = {
+ DataAttributeModelType,
+ "mag",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_q,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag_f,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_CONSTRUCTED,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f = {
+ DataAttributeModelType,
+ "f",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_FLOAT32,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_MX,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_SPCSO1 = {
+ DataObjectModelType,
+ "SPCSO1",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin = {
+ DataAttributeModelType,
+ "origin",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlNum,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat = {
+ DataAttributeModelType,
+ "orCat",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent = {
+ DataAttributeModelType,
+ "orIdent",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_OCTET_STRING_64,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum = {
+ DataAttributeModelType,
+ "ctlNum",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_INT8U,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper = {
+ DataAttributeModelType,
+ "Oper",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1,
+ NULL,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal = {
+ DataAttributeModelType,
+ "ctlVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin = {
+ DataAttributeModelType,
+ "origin",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat = {
+ DataAttributeModelType,
+ "orCat",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent = {
+ DataAttributeModelType,
+ "orIdent",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_OCTET_STRING_64,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum = {
+ DataAttributeModelType,
+ "ctlNum",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_T,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_INT8U,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T = {
+ DataAttributeModelType,
+ "T",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test = {
+ DataAttributeModelType,
+ "Test",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check = {
+ DataAttributeModelType,
+ "Check",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CHECK,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_SPCSO2 = {
+ DataObjectModelType,
+ "SPCSO2",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper = {
+ DataAttributeModelType,
+ "Oper",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal = {
+ DataAttributeModelType,
+ "ctlVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin = {
+ DataAttributeModelType,
+ "origin",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat = {
+ DataAttributeModelType,
+ "orCat",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent = {
+ DataAttributeModelType,
+ "orIdent",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_OCTET_STRING_64,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum = {
+ DataAttributeModelType,
+ "ctlNum",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_T,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_INT8U,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T = {
+ DataAttributeModelType,
+ "T",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test = {
+ DataAttributeModelType,
+ "Test",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check = {
+ DataAttributeModelType,
+ "Check",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CHECK,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_SPCSO3 = {
+ DataObjectModelType,
+ "SPCSO3",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper = {
+ DataAttributeModelType,
+ "Oper",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_ctlModel,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal = {
+ DataAttributeModelType,
+ "ctlVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin = {
+ DataAttributeModelType,
+ "origin",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat = {
+ DataAttributeModelType,
+ "orCat",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent = {
+ DataAttributeModelType,
+ "orIdent",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_OCTET_STRING_64,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum = {
+ DataAttributeModelType,
+ "ctlNum",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_T,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_INT8U,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T = {
+ DataAttributeModelType,
+ "T",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test = {
+ DataAttributeModelType,
+ "Test",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check = {
+ DataAttributeModelType,
+ "Check",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CHECK,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_SPCSO4 = {
+ DataObjectModelType,
+ "SPCSO4",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper = {
+ DataAttributeModelType,
+ "Oper",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_ctlModel,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal = {
+ DataAttributeModelType,
+ "ctlVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin = {
+ DataAttributeModelType,
+ "origin",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CONSTRUCTED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat = {
+ DataAttributeModelType,
+ "orCat",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent = {
+ DataAttributeModelType,
+ "orIdent",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_OCTET_STRING_64,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum = {
+ DataAttributeModelType,
+ "ctlNum",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_T,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_INT8U,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T = {
+ DataAttributeModelType,
+ "T",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test = {
+ DataAttributeModelType,
+ "Test",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_BOOLEAN,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check = {
+ DataAttributeModelType,
+ "Check",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CO,
+ IEC61850_CHECK,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel = {
+ DataAttributeModelType,
+ "ctlModel",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_CF,
+ IEC61850_ENUMERATED,
+ 0,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_Ind1 = {
+ DataObjectModelType,
+ "Ind1",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind1_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind1_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_Ind2 = {
+ DataObjectModelType,
+ "Ind2",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind2_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind2_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_Ind3 = {
+ DataObjectModelType,
+ "Ind3",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind3_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind3_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+DataObject iedModel_GenericIO_GGIO1_Ind4 = {
+ DataObjectModelType,
+ "Ind4",
+ (ModelNode*) &iedModel_GenericIO_GGIO1,
+ NULL,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_stVal,
+ 0,
+ -1
+};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal = {
+ DataAttributeModelType,
+ "stVal",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_q,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_BOOLEAN,
+ 0 + TRG_OPT_DATA_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind4_q = {
+ DataAttributeModelType,
+ "q",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4,
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_t,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_QUALITY,
+ 0 + TRG_OPT_QUALITY_CHANGED,
+ NULL,
+ 0};
+
+DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = {
+ DataAttributeModelType,
+ "t",
+ (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4,
+ NULL,
+ NULL,
+ 0,
+ -1,
+ IEC61850_FC_ST,
+ IEC61850_TIMESTAMP,
+ 0,
+ NULL,
+ 0};
+
+extern ReportControlBlock iedModel_GenericIO_LLN0_report0;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report1;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report2;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report3;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report4;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report5;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report6;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report7;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report8;
+extern ReportControlBlock iedModel_GenericIO_LLN0_report9;
+
+ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report1};
+ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCBPreConf01", "Events1", false, "Events", 1, 88, 175, 50, 1000, {0x4, 0xc0, 0xa8, 0x2, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report2};
+ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsBRCB01", "Events2", true, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report3};
+ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsBRCBPreConf01", "Events2", true, "Events", 1, 88, 175, 50, 1000, {0x4, 0xc0, 0xa8, 0x2, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report4};
+ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report5};
+ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report6};
+ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report7};
+ReportControlBlock iedModel_GenericIO_LLN0_report7 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report8};
+ReportControlBlock iedModel_GenericIO_LLN0_report8 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report9};
+ReportControlBlock iedModel_GenericIO_LLN0_report9 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL};
+
+
+
+
+
+
+
+IedModel iedModel = {
+ "simpleIO",
+ &iedModel_GenericIO,
+ &iedModelds_GenericIO_LLN0_Events,
+ &iedModel_GenericIO_LLN0_report0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ initializeValues
+};
+
+static void
+initializeValues()
+{
+
+iedModel_GenericIO_LLN0_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0);
+
+iedModel_GenericIO_LLN0_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_LLN0_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_LLN0_NamPlt_vendor.mmsValue = MmsValue_newVisibleString("MZ Automation");
+
+iedModel_GenericIO_LLN0_NamPlt_swRev.mmsValue = MmsValue_newVisibleString("1.3.0");
+
+iedModel_GenericIO_LLN0_NamPlt_d.mmsValue = MmsValue_newVisibleString("libiec61850 server example");
+
+iedModel_GenericIO_LPHD1_PhyHealth_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0);
+
+iedModel_GenericIO_GGIO1_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
+
+iedModel_GenericIO_GGIO1_SPCSO4_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1);
+}
diff --git a/examples/server_example_access_control/static_model.h b/examples/server_example_access_control/static_model.h
new file mode 100644
index 00000000..b6030e51
--- /dev/null
+++ b/examples/server_example_access_control/static_model.h
@@ -0,0 +1,311 @@
+/*
+ * static_model.h
+ *
+ * automatically generated from simpleIO_direct_control.cid
+ */
+
+#ifndef STATIC_MODEL_H_
+#define STATIC_MODEL_H_
+
+#include
+#include "iec61850_model.h"
+
+extern IedModel iedModel;
+extern LogicalDevice iedModel_GenericIO;
+extern LogicalNode iedModel_GenericIO_LLN0;
+extern DataObject iedModel_GenericIO_LLN0_Mod;
+extern DataAttribute iedModel_GenericIO_LLN0_Mod_stVal;
+extern DataAttribute iedModel_GenericIO_LLN0_Mod_q;
+extern DataAttribute iedModel_GenericIO_LLN0_Mod_t;
+extern DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel;
+extern DataObject iedModel_GenericIO_LLN0_Beh;
+extern DataAttribute iedModel_GenericIO_LLN0_Beh_stVal;
+extern DataAttribute iedModel_GenericIO_LLN0_Beh_q;
+extern DataAttribute iedModel_GenericIO_LLN0_Beh_t;
+extern DataObject iedModel_GenericIO_LLN0_Health;
+extern DataAttribute iedModel_GenericIO_LLN0_Health_stVal;
+extern DataAttribute iedModel_GenericIO_LLN0_Health_q;
+extern DataAttribute iedModel_GenericIO_LLN0_Health_t;
+extern DataObject iedModel_GenericIO_LLN0_NamPlt;
+extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor;
+extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev;
+extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_d;
+extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev;
+extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs;
+extern LogicalNode iedModel_GenericIO_LPHD1;
+extern DataObject iedModel_GenericIO_LPHD1_PhyNam;
+extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor;
+extern DataObject iedModel_GenericIO_LPHD1_PhyHealth;
+extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal;
+extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q;
+extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t;
+extern DataObject iedModel_GenericIO_LPHD1_Proxy;
+extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal;
+extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_q;
+extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_t;
+extern LogicalNode iedModel_GenericIO_GGIO1;
+extern DataObject iedModel_GenericIO_GGIO1_Mod;
+extern DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t;
+extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel;
+extern DataObject iedModel_GenericIO_GGIO1_Beh;
+extern DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Beh_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Beh_t;
+extern DataObject iedModel_GenericIO_GGIO1_Health;
+extern DataAttribute iedModel_GenericIO_GGIO1_Health_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Health_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Health_t;
+extern DataObject iedModel_GenericIO_GGIO1_NamPlt;
+extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor;
+extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev;
+extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d;
+extern DataObject iedModel_GenericIO_GGIO1_AnIn1;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t;
+extern DataObject iedModel_GenericIO_GGIO1_AnIn2;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t;
+extern DataObject iedModel_GenericIO_GGIO1_AnIn3;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t;
+extern DataObject iedModel_GenericIO_GGIO1_AnIn4;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t;
+extern DataObject iedModel_GenericIO_GGIO1_SPCSO1;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check;
+extern DataObject iedModel_GenericIO_GGIO1_SPCSO2;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t;
+extern DataObject iedModel_GenericIO_GGIO1_SPCSO3;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t;
+extern DataObject iedModel_GenericIO_GGIO1_SPCSO4;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel;
+extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t;
+extern DataObject iedModel_GenericIO_GGIO1_Ind1;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_t;
+extern DataObject iedModel_GenericIO_GGIO1_Ind2;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_t;
+extern DataObject iedModel_GenericIO_GGIO1_Ind3;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_t;
+extern DataObject iedModel_GenericIO_GGIO1_Ind4;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q;
+extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t;
+
+
+
+#define IEDMODEL_GenericIO (&iedModel_GenericIO)
+#define IEDMODEL_GenericIO_LLN0 (&iedModel_GenericIO_LLN0)
+#define IEDMODEL_GenericIO_LLN0_Mod (&iedModel_GenericIO_LLN0_Mod)
+#define IEDMODEL_GenericIO_LLN0_Mod_stVal (&iedModel_GenericIO_LLN0_Mod_stVal)
+#define IEDMODEL_GenericIO_LLN0_Mod_q (&iedModel_GenericIO_LLN0_Mod_q)
+#define IEDMODEL_GenericIO_LLN0_Mod_t (&iedModel_GenericIO_LLN0_Mod_t)
+#define IEDMODEL_GenericIO_LLN0_Mod_ctlModel (&iedModel_GenericIO_LLN0_Mod_ctlModel)
+#define IEDMODEL_GenericIO_LLN0_Beh (&iedModel_GenericIO_LLN0_Beh)
+#define IEDMODEL_GenericIO_LLN0_Beh_stVal (&iedModel_GenericIO_LLN0_Beh_stVal)
+#define IEDMODEL_GenericIO_LLN0_Beh_q (&iedModel_GenericIO_LLN0_Beh_q)
+#define IEDMODEL_GenericIO_LLN0_Beh_t (&iedModel_GenericIO_LLN0_Beh_t)
+#define IEDMODEL_GenericIO_LLN0_Health (&iedModel_GenericIO_LLN0_Health)
+#define IEDMODEL_GenericIO_LLN0_Health_stVal (&iedModel_GenericIO_LLN0_Health_stVal)
+#define IEDMODEL_GenericIO_LLN0_Health_q (&iedModel_GenericIO_LLN0_Health_q)
+#define IEDMODEL_GenericIO_LLN0_Health_t (&iedModel_GenericIO_LLN0_Health_t)
+#define IEDMODEL_GenericIO_LLN0_NamPlt (&iedModel_GenericIO_LLN0_NamPlt)
+#define IEDMODEL_GenericIO_LLN0_NamPlt_vendor (&iedModel_GenericIO_LLN0_NamPlt_vendor)
+#define IEDMODEL_GenericIO_LLN0_NamPlt_swRev (&iedModel_GenericIO_LLN0_NamPlt_swRev)
+#define IEDMODEL_GenericIO_LLN0_NamPlt_d (&iedModel_GenericIO_LLN0_NamPlt_d)
+#define IEDMODEL_GenericIO_LLN0_NamPlt_configRev (&iedModel_GenericIO_LLN0_NamPlt_configRev)
+#define IEDMODEL_GenericIO_LLN0_NamPlt_ldNs (&iedModel_GenericIO_LLN0_NamPlt_ldNs)
+#define IEDMODEL_GenericIO_LPHD1 (&iedModel_GenericIO_LPHD1)
+#define IEDMODEL_GenericIO_LPHD1_PhyNam (&iedModel_GenericIO_LPHD1_PhyNam)
+#define IEDMODEL_GenericIO_LPHD1_PhyNam_vendor (&iedModel_GenericIO_LPHD1_PhyNam_vendor)
+#define IEDMODEL_GenericIO_LPHD1_PhyHealth (&iedModel_GenericIO_LPHD1_PhyHealth)
+#define IEDMODEL_GenericIO_LPHD1_PhyHealth_stVal (&iedModel_GenericIO_LPHD1_PhyHealth_stVal)
+#define IEDMODEL_GenericIO_LPHD1_PhyHealth_q (&iedModel_GenericIO_LPHD1_PhyHealth_q)
+#define IEDMODEL_GenericIO_LPHD1_PhyHealth_t (&iedModel_GenericIO_LPHD1_PhyHealth_t)
+#define IEDMODEL_GenericIO_LPHD1_Proxy (&iedModel_GenericIO_LPHD1_Proxy)
+#define IEDMODEL_GenericIO_LPHD1_Proxy_stVal (&iedModel_GenericIO_LPHD1_Proxy_stVal)
+#define IEDMODEL_GenericIO_LPHD1_Proxy_q (&iedModel_GenericIO_LPHD1_Proxy_q)
+#define IEDMODEL_GenericIO_LPHD1_Proxy_t (&iedModel_GenericIO_LPHD1_Proxy_t)
+#define IEDMODEL_GenericIO_GGIO1 (&iedModel_GenericIO_GGIO1)
+#define IEDMODEL_GenericIO_GGIO1_Mod (&iedModel_GenericIO_GGIO1_Mod)
+#define IEDMODEL_GenericIO_GGIO1_Mod_stVal (&iedModel_GenericIO_GGIO1_Mod_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Mod_q (&iedModel_GenericIO_GGIO1_Mod_q)
+#define IEDMODEL_GenericIO_GGIO1_Mod_t (&iedModel_GenericIO_GGIO1_Mod_t)
+#define IEDMODEL_GenericIO_GGIO1_Mod_ctlModel (&iedModel_GenericIO_GGIO1_Mod_ctlModel)
+#define IEDMODEL_GenericIO_GGIO1_Beh (&iedModel_GenericIO_GGIO1_Beh)
+#define IEDMODEL_GenericIO_GGIO1_Beh_stVal (&iedModel_GenericIO_GGIO1_Beh_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Beh_q (&iedModel_GenericIO_GGIO1_Beh_q)
+#define IEDMODEL_GenericIO_GGIO1_Beh_t (&iedModel_GenericIO_GGIO1_Beh_t)
+#define IEDMODEL_GenericIO_GGIO1_Health (&iedModel_GenericIO_GGIO1_Health)
+#define IEDMODEL_GenericIO_GGIO1_Health_stVal (&iedModel_GenericIO_GGIO1_Health_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Health_q (&iedModel_GenericIO_GGIO1_Health_q)
+#define IEDMODEL_GenericIO_GGIO1_Health_t (&iedModel_GenericIO_GGIO1_Health_t)
+#define IEDMODEL_GenericIO_GGIO1_NamPlt (&iedModel_GenericIO_GGIO1_NamPlt)
+#define IEDMODEL_GenericIO_GGIO1_NamPlt_vendor (&iedModel_GenericIO_GGIO1_NamPlt_vendor)
+#define IEDMODEL_GenericIO_GGIO1_NamPlt_swRev (&iedModel_GenericIO_GGIO1_NamPlt_swRev)
+#define IEDMODEL_GenericIO_GGIO1_NamPlt_d (&iedModel_GenericIO_GGIO1_NamPlt_d)
+#define IEDMODEL_GenericIO_GGIO1_AnIn1 (&iedModel_GenericIO_GGIO1_AnIn1)
+#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag (&iedModel_GenericIO_GGIO1_AnIn1_mag)
+#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f (&iedModel_GenericIO_GGIO1_AnIn1_mag_f)
+#define IEDMODEL_GenericIO_GGIO1_AnIn1_q (&iedModel_GenericIO_GGIO1_AnIn1_q)
+#define IEDMODEL_GenericIO_GGIO1_AnIn1_t (&iedModel_GenericIO_GGIO1_AnIn1_t)
+#define IEDMODEL_GenericIO_GGIO1_AnIn2 (&iedModel_GenericIO_GGIO1_AnIn2)
+#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag (&iedModel_GenericIO_GGIO1_AnIn2_mag)
+#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f (&iedModel_GenericIO_GGIO1_AnIn2_mag_f)
+#define IEDMODEL_GenericIO_GGIO1_AnIn2_q (&iedModel_GenericIO_GGIO1_AnIn2_q)
+#define IEDMODEL_GenericIO_GGIO1_AnIn2_t (&iedModel_GenericIO_GGIO1_AnIn2_t)
+#define IEDMODEL_GenericIO_GGIO1_AnIn3 (&iedModel_GenericIO_GGIO1_AnIn3)
+#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag (&iedModel_GenericIO_GGIO1_AnIn3_mag)
+#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f (&iedModel_GenericIO_GGIO1_AnIn3_mag_f)
+#define IEDMODEL_GenericIO_GGIO1_AnIn3_q (&iedModel_GenericIO_GGIO1_AnIn3_q)
+#define IEDMODEL_GenericIO_GGIO1_AnIn3_t (&iedModel_GenericIO_GGIO1_AnIn3_t)
+#define IEDMODEL_GenericIO_GGIO1_AnIn4 (&iedModel_GenericIO_GGIO1_AnIn4)
+#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag (&iedModel_GenericIO_GGIO1_AnIn4_mag)
+#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f (&iedModel_GenericIO_GGIO1_AnIn4_mag_f)
+#define IEDMODEL_GenericIO_GGIO1_AnIn4_q (&iedModel_GenericIO_GGIO1_AnIn4_q)
+#define IEDMODEL_GenericIO_GGIO1_AnIn4_t (&iedModel_GenericIO_GGIO1_AnIn4_t)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1 (&iedModel_GenericIO_GGIO1_SPCSO1)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin (&iedModel_GenericIO_GGIO1_SPCSO1_origin)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_ctlNum)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal (&iedModel_GenericIO_GGIO1_SPCSO1_stVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_q (&iedModel_GenericIO_GGIO1_SPCSO1_q)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_t (&iedModel_GenericIO_GGIO1_SPCSO1_t)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper (&iedModel_GenericIO_GGIO1_SPCSO1_Oper)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_T)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2 (&iedModel_GenericIO_GGIO1_SPCSO2)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper (&iedModel_GenericIO_GGIO1_SPCSO2_Oper)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_T)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal (&iedModel_GenericIO_GGIO1_SPCSO3_stVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_q (&iedModel_GenericIO_GGIO1_SPCSO3_q)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_T)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO3_ctlModel)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO3_t (&iedModel_GenericIO_GGIO1_SPCSO3_t)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4 (&iedModel_GenericIO_GGIO1_SPCSO4)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal (&iedModel_GenericIO_GGIO1_SPCSO4_stVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_q (&iedModel_GenericIO_GGIO1_SPCSO4_q)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper (&iedModel_GenericIO_GGIO1_SPCSO4_Oper)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_T)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO4_ctlModel)
+#define IEDMODEL_GenericIO_GGIO1_SPCSO4_t (&iedModel_GenericIO_GGIO1_SPCSO4_t)
+#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1)
+#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q)
+#define IEDMODEL_GenericIO_GGIO1_Ind1_t (&iedModel_GenericIO_GGIO1_Ind1_t)
+#define IEDMODEL_GenericIO_GGIO1_Ind2 (&iedModel_GenericIO_GGIO1_Ind2)
+#define IEDMODEL_GenericIO_GGIO1_Ind2_stVal (&iedModel_GenericIO_GGIO1_Ind2_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Ind2_q (&iedModel_GenericIO_GGIO1_Ind2_q)
+#define IEDMODEL_GenericIO_GGIO1_Ind2_t (&iedModel_GenericIO_GGIO1_Ind2_t)
+#define IEDMODEL_GenericIO_GGIO1_Ind3 (&iedModel_GenericIO_GGIO1_Ind3)
+#define IEDMODEL_GenericIO_GGIO1_Ind3_stVal (&iedModel_GenericIO_GGIO1_Ind3_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Ind3_q (&iedModel_GenericIO_GGIO1_Ind3_q)
+#define IEDMODEL_GenericIO_GGIO1_Ind3_t (&iedModel_GenericIO_GGIO1_Ind3_t)
+#define IEDMODEL_GenericIO_GGIO1_Ind4 (&iedModel_GenericIO_GGIO1_Ind4)
+#define IEDMODEL_GenericIO_GGIO1_Ind4_stVal (&iedModel_GenericIO_GGIO1_Ind4_stVal)
+#define IEDMODEL_GenericIO_GGIO1_Ind4_q (&iedModel_GenericIO_GGIO1_Ind4_q)
+#define IEDMODEL_GenericIO_GGIO1_Ind4_t (&iedModel_GenericIO_GGIO1_Ind4_t)
+
+#endif /* STATIC_MODEL_H_ */
+
diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c
index 6023f3b9..3f1ca077 100644
--- a/examples/server_example_basic_io/server_example_basic_io.c
+++ b/examples/server_example_basic_io/server_example_basic_io.c
@@ -100,6 +100,12 @@ rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conne
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create new server configuration object */
@@ -163,7 +169,7 @@ main(int argc, char** argv)
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);
/* MMS server will be instructed to start listening for client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n");
diff --git a/examples/server_example_control/server_example_control.c b/examples/server_example_control/server_example_control.c
index 6159cdda..501acb46 100644
--- a/examples/server_example_control/server_example_control.c
+++ b/examples/server_example_control/server_example_control.c
@@ -126,6 +126,11 @@ int
main(int argc, char** argv)
{
iedServer = IedServer_create(&iedModel);
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,
(ControlHandler) controlHandlerForBinaryOutput,
@@ -169,7 +174,7 @@ main(int argc, char** argv)
IEDMODEL_GenericIO_GGIO1_SPCSO9);
/* MMS server will be instructed to start listening to client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_deadband/server_example_deadband.c b/examples/server_example_deadband/server_example_deadband.c
index 044b72e8..a93236dd 100644
--- a/examples/server_example_deadband/server_example_deadband.c
+++ b/examples/server_example_deadband/server_example_deadband.c
@@ -36,6 +36,12 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create new server configuration object */
@@ -74,7 +80,7 @@ main(int argc, char** argv)
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW);
/* MMS server will be instructed to start listening for client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n");
diff --git a/examples/server_example_files/server_example_files.c b/examples/server_example_files/server_example_files.c
index 702eed59..9974fe6f 100644
--- a/examples/server_example_files/server_example_files.c
+++ b/examples/server_example_files/server_example_files.c
@@ -55,6 +55,12 @@ fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServi
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
iedServer = IedServer_create(&iedModel);
@@ -70,7 +76,7 @@ main(int argc, char** argv)
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c
index 01ad5ae2..ee388be8 100644
--- a/examples/server_example_logging/server_example_logging.c
+++ b/examples/server_example_logging/server_example_logging.c
@@ -115,9 +115,37 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in
return true;
}
+static bool
+lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation)
+{
+ printf("%s access to LCB %s from %s\n", operation == LCB_EVENT_GET_PARAMETER ? "read" : "write", LogControlBlock_getName(lcb), ClientConnection_getPeerAddress(connection));
+
+ /* only allow read access */
+ if (operation == LCB_EVENT_GET_PARAMETER) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+static bool
+logAccessHandler(void* parameter, const char* logName, ClientConnection connection)
+{
+ printf("Access to log %s from %s\n", logName, ClientConnection_getPeerAddress(connection));
+
+ return false;
+}
+
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
iedServer = IedServer_create(&iedModel);
@@ -141,6 +169,10 @@ main(int argc, char** argv)
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
+ IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL);
+
+ IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL);
+
LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db");
LogStorage_setMaxLogEntries(statusLog, 10);
@@ -174,7 +206,7 @@ main(int argc, char** argv)
/* MMS server will be instructed to start listening to client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_password_auth/server_example_password_auth.c b/examples/server_example_password_auth/server_example_password_auth.c
index 2327923f..12938aa1 100644
--- a/examples/server_example_password_auth/server_example_password_auth.c
+++ b/examples/server_example_password_auth/server_example_password_auth.c
@@ -159,6 +159,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu
int main(int argc, char** argv) {
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
iedServer = IedServer_create(&iedModel);
/* Activate authentication */
@@ -194,7 +200,7 @@ int main(int argc, char** argv) {
IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_setting_groups/server_example_sg.c b/examples/server_example_setting_groups/server_example_sg.c
index 4466c1ad..cb3e2ec7 100644
--- a/examples/server_example_setting_groups/server_example_sg.c
+++ b/examples/server_example_setting_groups/server_example_sg.c
@@ -97,7 +97,10 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu
{
void* securityToken = ClientConnection_getSecurityToken(connection);
- printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name);
+ if (dataObject)
+ printf("Read access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject->name, FunctionalConstraint_toString(fc));
+ else
+ printf("Read access to %s/%s[%s]\n", ld->name, ln->name, FunctionalConstraint_toString(fc));
return DATA_ACCESS_ERROR_SUCCESS;
}
@@ -105,6 +108,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
IedServerConfig config = IedServerConfig_create();
//IedServerConfig_enableEditSG(config, false);
@@ -112,6 +121,8 @@ main(int argc, char** argv)
iedServer = IedServer_createWithConfig(&iedModel, NULL, config);
+ IedServer_setTimeQuality(iedServer, true, false, false, 10);
+
IedServerConfig_destroy(config);
LogicalDevice* ld = IEDMODEL_PROT;
@@ -127,7 +138,7 @@ main(int argc, char** argv)
IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c
index c8292dee..e91ff199 100644
--- a/examples/server_example_substitution/server_example_substitution.c
+++ b/examples/server_example_substitution/server_example_substitution.c
@@ -200,6 +200,12 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create a new IEC 61850 server instance */
@@ -220,7 +226,7 @@ main(int argc, char** argv)
IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_blkEna, writeAccessHandler, NULL);
/* MMS server will be instructed to start listening for client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_threadless/server_example_threadless.c b/examples/server_example_threadless/server_example_threadless.c
index ae63ce62..13f76927 100644
--- a/examples/server_example_threadless/server_example_threadless.c
+++ b/examples/server_example_threadless/server_example_threadless.c
@@ -65,6 +65,11 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
iedServer = IedServer_create(&iedModel);
@@ -86,7 +91,7 @@ main(int argc, char** argv)
IEDMODEL_GenericIO_GGIO1_SPCSO4);
/* MMS server will be instructed to start listening to client connections. */
- IedServer_startThreadless(iedServer, 102);
+ IedServer_startThreadless(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n");
diff --git a/examples/server_example_write_handler/server_example_write_handler.c b/examples/server_example_write_handler/server_example_write_handler.c
index 49151f06..41e04f4c 100644
--- a/examples/server_example_write_handler/server_example_write_handler.c
+++ b/examples/server_example_write_handler/server_example_write_handler.c
@@ -41,10 +41,16 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
int
main(int argc, char** argv)
{
+ int tcpPort = 102;
+
+ if (argc > 1) {
+ tcpPort = atoi(argv[1]);
+ }
+
iedServer = IedServer_create(&iedModel);
/* MMS server will be instructed to start listening to client connections. */
- IedServer_start(iedServer, 102);
+ IedServer_start(iedServer, tcpPort);
/* Don't allow access to SP variables by default */
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_SP, ACCESS_POLICY_DENY);
diff --git a/examples/tls_client_example/tls_client_example.c b/examples/tls_client_example/tls_client_example.c
index c546f425..db730f3f 100644
--- a/examples/tls_client_example/tls_client_example.c
+++ b/examples/tls_client_example/tls_client_example.c
@@ -30,11 +30,15 @@ reportCallbackFunction(void* parameter, ClientReport report)
}
static void
-securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg)
+securityEventHandler(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* msg, TLSConnection con)
{
(void)parameter;
- printf("[SECURITY EVENT] %s (t: %i, c: %i)\n", msg, eventLevel, eventCode);
+ char* peerAddr = TLSConnection_getPeerAddress(con, NULL);
+
+ printf("[SECURITY EVENT] %s (%s)(t: %i, c: %i)\n", msg, peerAddr, eventLevel, eventCode);
+
+ free(peerAddr);
}
int main(int argc, char** argv) {
diff --git a/examples/tls_server_example/tls_server_example.c b/examples/tls_server_example/tls_server_example.c
index 08bfd662..6db25222 100644
--- a/examples/tls_server_example/tls_server_example.c
+++ b/examples/tls_server_example/tls_server_example.c
@@ -103,11 +103,17 @@ clientAuthenticator(void* parameter, AcseAuthenticationParameter authParameter,
}
static void
-securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg)
+securityEventHandler(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* msg, TLSConnection con)
{
(void)parameter;
- printf("[SECURITY EVENT] %s (t: %i, c: %i)\n", msg, eventLevel, eventCode);
+ char* peerAddr = TLSConnection_getPeerAddress(con, NULL);
+
+ const char* tlsVersionStr = TLSConfigVersion_toString(TLSConnection_getTLSVersion(con));
+
+ printf("[SECURITY EVENT - %s] %s (%s)(t: %i, c: %i)\n", tlsVersionStr, msg, peerAddr, eventLevel, eventCode);
+
+ free(peerAddr);
}
int
diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt
index 33975c28..9b421f6b 100644
--- a/hal/CMakeLists.txt
+++ b/hal/CMakeLists.txt
@@ -10,7 +10,7 @@ endif()
project(hal)
set(LIBHAL_VERSION_MAJOR "2")
-set(LIBHAL_VERSION_MINOR "0")
+set(LIBHAL_VERSION_MINOR "1")
set(LIBHAL_VERSION_PATCH "0")
# feature checks
@@ -129,7 +129,7 @@ include_directories(
if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH})
else()
-file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/library/*.c)
+file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.28/library/*.c)
endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h")
diff --git a/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c
index 2d97a826..eaf1897a 100644
--- a/hal/ethernet/linux/ethernet_linux.c
+++ b/hal/ethernet/linux/ethernet_linux.c
@@ -263,6 +263,8 @@ void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{
struct packet_mreq mreq;
+ memset(&mreq, 0, sizeof(struct packet_mreq));
+
mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex;
mreq.mr_alen = ETH_ALEN;
mreq.mr_type = PACKET_MR_MULTICAST;
diff --git a/hal/inc/hal_socket.h b/hal/inc/hal_socket.h
index 33aaf12c..c55ad19e 100644
--- a/hal/inc/hal_socket.h
+++ b/hal/inc/hal_socket.h
@@ -357,7 +357,7 @@ Socket_getPeerAddress(Socket self);
*
* The peer address has to be returned as null terminated string
*
- * Implementation of this function is MANDATORY (lib60870)
+ * Implementation of this function is MANDATORY (lib60870 and libiec61850)
*
* \param self the client, connection or server socket instance
* \param peerAddressString a string to store the peer address (the string should have space
diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h
index 37af40c2..2896a108 100644
--- a/hal/inc/tls_config.h
+++ b/hal/inc/tls_config.h
@@ -3,7 +3,7 @@
*
* TLS Configuration API for protocol stacks using TCP/IP
*
- * Copyright 2017-2021 Michael Zillgith
+ * Copyright 2017-2022 Michael Zillgith
*
* Abstraction layer for configuration of different TLS implementations
*
@@ -50,11 +50,30 @@ TLSConfiguration_create(void);
PAL_API void
TLSConfiguration_setClientMode(TLSConfiguration self);
+typedef enum {
+ TLS_VERSION_NOT_SELECTED = 0,
+ TLS_VERSION_SSL_3_0 = 3,
+ TLS_VERSION_TLS_1_0 = 4,
+ TLS_VERSION_TLS_1_1 = 5,
+ TLS_VERSION_TLS_1_2 = 6,
+ TLS_VERSION_TLS_1_3 = 7
+} TLSConfigVersion;
+
+/**
+ * \brief Convert TLS version number to string
+ *
+ * \param version TLS version number
+ *
+ * \return the TLS version as null terminated string
+ */
+PAL_API const char*
+TLSConfigVersion_toString(TLSConfigVersion version);
+
typedef enum {
TLS_SEC_EVT_INFO = 0,
TLS_SEC_EVT_WARNING = 1,
TLS_SEC_EVT_INCIDENT = 2
-} TLSConfiguration_EventLevel;
+} TLSEventLevel;
#define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1
#define TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2
@@ -69,12 +88,50 @@ typedef enum {
#define TLS_EVENT_CODE_ALM_CERT_EXPIRED 11
#define TLS_EVENT_CODE_ALM_CERT_REVOKED 12
#define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13
-#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 12
+#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 14
+#define TLS_EVENT_CODE_ALM_NO_CIPHER 15
+
+typedef struct sTLSConnection* TLSConnection;
+
+/**
+ * \brief Get the peer address of the TLS connection
+ *
+ * \param self the TLS connection instance
+ * \param peerAddrBuf user provided buffer that can hold at least 60 characters, or NULL to allow the function to allocate the memory for the buffer
+ *
+ * \returns peer address:port as null terminated string
+ */
+PAL_API char*
+TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf);
+
+/**
+ * \brief Get the TLS certificate used by the peer
+ *
+ * \param self the TLS connection instance
+ * \param certSize[OUT] the certificate size in bytes
+ *
+ * \return address of the certificate buffer
+ */
+PAL_API uint8_t*
+TLSConnection_getPeerCertificate(TLSConnection self, int* certSize);
-typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* message);
+/**
+ * \brief Get the TLS version used by the connection
+ *
+ * \param self the TLS connection instance
+ *
+ * \return TLS version
+ */
+PAL_API TLSConfigVersion
+TLSConnection_getTLSVersion(TLSConnection self);
+
+typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* message, TLSConnection con);
/**
* \brief Set the security event handler
+ *
+ * \param handler the security event callback handler
+ * \param parameter user provided parameter to be passed to the callback handler
*/
PAL_API void
TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter);
@@ -209,15 +266,6 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
PAL_API void
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs);
-typedef enum {
- TLS_VERSION_NOT_SELECTED = 0,
- TLS_VERSION_SSL_3_0 = 3,
- TLS_VERSION_TLS_1_0 = 4,
- TLS_VERSION_TLS_1_1 = 5,
- TLS_VERSION_TLS_1_2 = 6,
- TLS_VERSION_TLS_1_3 = 7
-} TLSConfigVersion;
-
/**
* \brief Set minimal allowed TLS version to use
*/
@@ -249,6 +297,12 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen);
PAL_API bool
TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename);
+/**
+ * \brief Removes any CRL (certificate revocation list) currently in use
+ */
+PAL_API void
+TLSConfiguration_resetCRL(TLSConfiguration self);
+
/**
* Release all resource allocated by the TLSConfiguration instance
*
diff --git a/hal/memory/lib_memory.c b/hal/memory/lib_memory.c
index adeb105a..74fde5b4 100644
--- a/hal/memory/lib_memory.c
+++ b/hal/memory/lib_memory.c
@@ -38,7 +38,6 @@ Memory_malloc(size_t size)
return memory;
}
-
void*
Memory_calloc(size_t nmemb, size_t size)
{
@@ -50,7 +49,6 @@ Memory_calloc(size_t nmemb, size_t size)
return memory;
}
-
void *
Memory_realloc(void *ptr, size_t size)
{
@@ -67,4 +65,3 @@ Memory_free(void* memb)
{
free(memb);
}
-
diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c
index 65c96538..e2f152d0 100644
--- a/hal/tls/mbedtls/tls_mbedtls.c
+++ b/hal/tls/mbedtls/tls_mbedtls.c
@@ -105,10 +105,10 @@ struct sTLSSocket {
};
static void
-raiseSecurityEvent(TLSConfiguration config, TLSConfiguration_EventLevel eventCategory, int eventCode, const char* message)
+raiseSecurityEvent(TLSConfiguration config, TLSEventLevel eventCategory, int eventCode, const char* message, TLSSocket socket)
{
if (config->eventHandler) {
- config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message);
+ config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message, (TLSConnection)socket);
}
}
@@ -174,7 +174,7 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth
if (certMatches)
*flags = 0;
else {
- raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured");
+ raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: certificate validation: trusted individual certificate not available", self);
*flags |= MBEDTLS_X509_BADCERT_OTHER;
return 1;
@@ -214,7 +214,7 @@ TLSConfiguration_setupComplete(TLSConfiguration self)
int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey));
if (ret != 0) {
- DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned -0x%x\n", -ret);
return false;
}
}
@@ -265,7 +265,7 @@ TLSConfiguration_create()
mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
- mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED);
+ mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION);
/* static int hashes[] = {3,4,5,6,7,8,0}; */
/* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */
@@ -349,7 +349,7 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate,
int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen);
if (ret != 0)
- DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret);
return (ret == 0);
}
@@ -360,7 +360,7 @@ TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* fi
int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename);
if (ret != 0)
- DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned -0x%x\n", -ret);
return (ret == 0);
}
@@ -371,7 +371,7 @@ TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, cons
int ret = mbedtls_pk_parse_key(&(self->ownKey), key, keyLen, (const uint8_t*) keyPassword, (keyPassword == NULL) ? 0 : strlen(keyPassword));
if (ret != 0)
- DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned -0x%x\n", -ret);
return (ret == 0);
}
@@ -382,7 +382,7 @@ TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename,
int ret = mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword);
if (ret != 0)
- DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned -0x%x\n", -ret);
return (ret == 0);
}
@@ -427,7 +427,7 @@ TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, i
int ret = mbedtls_x509_crt_parse(&(self->cacerts), certificate, certLen);
if (ret != 0) {
- DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret);
return false;
}
@@ -440,21 +440,41 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename);
if (ret != 0)
- DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret);
return (ret == 0);
}
+static void
+udpatedCRL(TLSConfiguration self)
+{
+ self->crlUpdated = Hal_getTimeInMs();
+
+ /* We need to clean-up resumption cache (if enabled) to make sure we renegotiate as CRL may have changed data */
+ if (self->useSessionResumption == false)
+ return;
+
+ if (self->conf.endpoint == MBEDTLS_SSL_IS_SERVER)
+ {
+ mbedtls_ssl_cache_entry *cur = self->cache.chain;
+
+ while (cur) {
+ cur->timestamp = 0;
+ cur = cur->next;
+ }
+ }
+}
+
bool
TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen)
{
int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen);
if (ret != 0) {
- DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret);
}
else {
- self->crlUpdated = Hal_getTimeInMs();
+ udpatedCRL(self);
}
return (ret == 0);
@@ -469,12 +489,20 @@ TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename)
DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret);
}
else {
- self->crlUpdated = Hal_getTimeInMs();
+ udpatedCRL(self);
}
return (ret == 0);
}
+void
+TLSConfiguration_resetCRL(TLSConfiguration self)
+{
+ mbedtls_x509_crl_free(&(self->crl));
+ mbedtls_x509_crl_init(&(self->crl));
+ self->crlUpdated = Hal_getTimeInMs();
+}
+
void
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs)
{
@@ -518,63 +546,72 @@ TLSConfiguration_destroy(TLSConfiguration self)
}
static void
-createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags)
+createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket socket)
{
if (config->eventHandler == NULL)
return;
switch (ret) {
+ case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG:
+ raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: Algorithm not supported", socket);
+ break;
+
+ case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN:
+ raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: no matching TLS ciphers", socket);
+ break;
case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE:
- raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Incident: Algorithm not supported");
+ raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Alarm: Algorithm not supported", socket);
break;
case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Incident: Unsecure communication");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Alarm: Unsecure communication", socket);
break;
case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Incident: Certificate unavailable");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Alarm: certificate unavailable", socket);
break;
case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Incident: Bad certificate");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Alarm: Bad certificate", socket);
break;
case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Incident: TLS certificate size exceeded");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Alarm: TLS certificate size exceeded", socket);
break;
case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: certificate validation: certificate signature could not be validated");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: certificate validation: certificate signature could not be validated", socket);
break;
case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Incident: Certificate required");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Alarm: Certificate required", socket);
break;
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
{
if (flags & MBEDTLS_X509_BADCERT_EXPIRED) {
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Incident: Certificate expired");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Alarm: expired certificate", socket);
}
else if (flags & MBEDTLS_X509_BADCERT_REVOKED) {
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Incident: Certificate revoked");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Alarm: revoked certificate", socket);
}
else if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) {
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Incident: Certificate validation: CA certificate not available");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Alarm: Certificate validation: CA certificate not available", socket);
}
else if (flags & MBEDTLS_X509_BADCERT_OTHER) {
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Certificate not configured", socket);
}
- else {
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: Certificate verification failed");
+ else if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) {
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Insufficient key length", socket);
}
+
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: Certificate verification failed", socket);
}
break;
default:
- raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Incident: handshake failed for unknown reason");
+ raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Alarm: handshake failed for unknown reason", socket);
break;
}
}
@@ -749,11 +786,11 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
{
if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE )
{
- DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake --> %d\n\n", ret );
+ DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake returned -0x%x\n", -ret );
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
- createSecurityEvents(configuration, ret, flags);
+ createSecurityEvents(configuration, ret, flags, self);
mbedtls_ssl_free(&(self->ssl));
@@ -793,7 +830,7 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
self->lastRenegotiationTime = Hal_getTimeInMs();
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) {
- raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version");
+ raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self);
}
}
@@ -817,7 +854,7 @@ TLSSocket_performHandshake(TLSSocket self)
if (ret == 0) {
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) {
- raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version");
+ raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self);
}
return true;
@@ -828,38 +865,59 @@ TLSSocket_performHandshake(TLSSocket self)
if (self->tlsConfig->eventHandler) {
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
- createSecurityEvents(self->tlsConfig, ret, flags);
+ createSecurityEvents(self->tlsConfig, ret, flags, self);
}
return false;
}
}
-int
-TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
+static void
+checkForCRLUpdate(TLSSocket self)
{
- if (self->crlUpdated != self->tlsConfig->crlUpdated) {
- DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n");
+ if (self->crlUpdated == self->tlsConfig->crlUpdated)
+ return;
+
+ DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n");
+
+ mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) );
- mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) );
+ self->crlUpdated = self->tlsConfig->crlUpdated;
+
+ /* IEC TS 62351-100-3 Conformance test 6.2.6 requires that upon CRL update a TLS renegotiation should occur */
+ self->lastRenegotiationTime = 0;
+}
- self->crlUpdated = self->tlsConfig->crlUpdated;
+/* true = renegotiation is not needed or it is successfull, false = Failed */
+static bool
+startRenegotiationIfRequired(TLSSocket self)
+{
+ if (self->tlsConfig->renegotiationTimeInMs <= 0)
+ return true;
+
+ if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs)
+ return true;
+
+ raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self);
+
+ if (TLSSocket_performHandshake(self) == false) {
+ DEBUG_PRINT("TLS", " renegotiation failed\n");
+ return false;
}
- if (self->tlsConfig->renegotiationTimeInMs > 0) {
- if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) {
+ DEBUG_PRINT("TLS", " started renegotiation\n");
+ self->lastRenegotiationTime = Hal_getTimeInMs();
+
+ return true;
+}
- raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started");
+int
+TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
+{
+ checkForCRLUpdate(self);
- if (TLSSocket_performHandshake(self) == false) {
- DEBUG_PRINT("TLS", " renegotiation failed\n");
- return -1;
- }
- else {
- DEBUG_PRINT("TLS", " started renegotiation\n");
- self->lastRenegotiationTime = Hal_getTimeInMs();
- }
- }
+ if (startRenegotiationIfRequired(self) == false) {
+ return -1;
}
int ret = mbedtls_ssl_read(&(self->ssl), buf, size);
@@ -885,7 +943,7 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size)
{
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
- createSecurityEvents(self->tlsConfig, ret, flags);
+ createSecurityEvents(self->tlsConfig, ret, flags, self);
}
return -1;
@@ -901,28 +959,10 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size)
int ret;
int len = size;
- if (self->crlUpdated != self->tlsConfig->crlUpdated) {
- DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n");
-
- mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) );
+ checkForCRLUpdate(self);
- self->crlUpdated = self->tlsConfig->crlUpdated;
- }
-
- if (self->tlsConfig->renegotiationTimeInMs > 0) {
- if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) {
-
- raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started");
-
- if (TLSSocket_performHandshake(self) == false) {
- DEBUG_PRINT("TLS", " renegotiation failed\n");
- return -1;
- }
- else {
- DEBUG_PRINT("TLS", " started renegotiation\n");
- self->lastRenegotiationTime = Hal_getTimeInMs();
- }
- }
+ if (startRenegotiationIfRequired(self) == false) {
+ return -1;
}
while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0)
@@ -956,7 +996,7 @@ TLSSocket_close(TLSSocket self)
{
if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE))
{
- DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret);
+ DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned -0x%x\n", -ret);
break;
}
}
@@ -970,3 +1010,54 @@ TLSSocket_close(TLSSocket self)
GLOBAL_FREEMEM(self);
}
+
+char*
+TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf)
+{
+ TLSSocket socket = (TLSSocket)self;
+
+ if (peerAddrBuf == NULL) {
+ peerAddrBuf = (char*)GLOBAL_MALLOC(61);
+ }
+
+ if (peerAddrBuf)
+ return Socket_getPeerAddressStatic(socket->socket, peerAddrBuf);
+ else
+ return NULL;
+}
+
+uint8_t*
+TLSConnection_getPeerCertificate(TLSConnection self, int* certSize)
+{
+ TLSSocket socket = (TLSSocket)self;
+
+ return TLSSocket_getPeerCertificate(socket, certSize);
+}
+
+TLSConfigVersion
+TLSConnection_getTLSVersion(TLSConnection self)
+{
+ TLSSocket socket = (TLSSocket)self;
+
+ return getTLSVersion(socket->ssl.major_ver, socket->ssl.minor_ver);
+}
+
+const char*
+TLSConfigVersion_toString(TLSConfigVersion version)
+{
+ switch (version)
+ {
+ case TLS_VERSION_SSL_3_0:
+ return "SSL 3.0";
+ case TLS_VERSION_TLS_1_0:
+ return "TLS 1.0";
+ case TLS_VERSION_TLS_1_1:
+ return "TLS 1.1";
+ case TLS_VERSION_TLS_1_2:
+ return "TLS 1.2";
+ case TLS_VERSION_TLS_1_3:
+ return "TLS 1.3";
+ default:
+ return "unknown TLS version";
+ }
+}
diff --git a/pyiec61850/examples/rcbSubscriptionExample.py b/pyiec61850/examples/rcbSubscriptionExample.py
new file mode 100644
index 00000000..d4d05aa5
--- /dev/null
+++ b/pyiec61850/examples/rcbSubscriptionExample.py
@@ -0,0 +1,260 @@
+#!/usr/bin/python
+
+'''
+Example of RCB subscription, with the Python wrapper
+
+This example is intended to be used with server_example_basic_io.
+
+Usage:
+ 'sudo python3 ./pyiec61850/examples/rcbSubscriptionExample.py'
+ or
+ 'sudo python3 ./pyiec61850/examples/rcbSubscriptionExample.py localhost 102'
+ or
+ 'python3 ./pyiec61850/examples/rcbSubscriptionExample.py localhost 8102'
+
+
+Swig generates 2 wrapped objects:
+ - a generic RCB handler
+ - a generic RCB subscriber
+
+The user needs to:
+ - create his specific RCB handler in Python,
+ for processing the received RCB as he wants
+ - create his specific RCB subscriber in Python,
+ with his registering parameters
+ - connect his handler to his subscriber with a composition relationship
+'''
+
+import time
+import sys
+import iec61850
+
+
+def open_connection(ip_address, mms_port):
+ '''Open the connection with the IED'''
+ l_connection = iec61850.IedConnection_create()
+ l_error = iec61850.IedConnection_connect(l_connection, ip_address, mms_port)
+ return l_error, l_connection
+
+
+def close_connection(i_connection):
+ '''Close the connection with the IED'''
+ iec61850.IedConnection_close(i_connection)
+
+
+def destroy_connection(i_connection):
+ '''Destroy (free) the Connection object'''
+ iec61850.IedConnection_destroy(i_connection)
+
+
+class PyRCBHandler(iec61850.RCBHandler):
+ '''Class processing the received RCB'''
+
+ def __init__(self):
+ iec61850.RCBHandler.__init__(self)
+
+ def trigger(self):
+ '''Method for triggering the handler and processing the last received RCB.
+ In these example, we only print some attributs and data of the RCB'''
+
+ # the following section is the application part of the Swig C subthread:
+ # we must catch the Python exceptions, otherwise it will crash.
+ try:
+ l_rcb_ref = iec61850.ClientReport_getRcbReference(self._client_report)
+ print(f"\nNew received RCB: {l_rcb_ref}")
+
+ if iec61850.ClientReport_hasDataSetName(self._client_report):
+ l_dataset_name = iec61850.ClientReport_getDataSetName(self._client_report)
+ print(f"\tDataSet name: {l_dataset_name}")
+
+ print(f"\tReport id: {iec61850.ClientReport_getRptId(self._client_report)}")
+
+ if iec61850.ClientReport_hasSeqNum(self._client_report):
+ l_seq_num = iec61850.ClientReport_getSeqNum(self._client_report)
+ print(f"\tSequence num: {l_seq_num}")
+
+ if iec61850.ClientReport_hasSubSeqNum(self._client_report):
+ l_sub_seq_num = iec61850.ClientReport_getSubSeqNum(self._client_report)
+ print(f"\tSub-sequence num: {l_sub_seq_num}")
+
+ if iec61850.ClientReport_hasTimestamp(self._client_report):
+ l_timestamp_millisec =iec61850.ClientReport_getTimestamp(self._client_report)
+ print(f"\tTimestamp in millsec: {l_timestamp_millisec}")
+
+ mms_value_array = iec61850.ClientReport_getDataSetValues(self._client_report)
+ mms_value_array_size = iec61850.MmsValue_getArraySize(mms_value_array)
+ print(f"\tDataSet size: {mms_value_array_size}")
+
+ for mms_value_index in range(mms_value_array_size):
+ mms_value = iec61850.MmsValue_getElement(mms_value_array, mms_value_index)
+ mms_value_type = iec61850.MmsValue_getTypeString(mms_value)
+
+ if mms_value_type == "boolean":
+ print(f"\tMMS value: {iec61850.MmsValue_getBoolean(mms_value)}")
+ elif mms_value_type == "float":
+ print(f"\tMMS value: {iec61850.MmsValue_toFloat(mms_value)}")
+ else:
+ print("\tMMS value: other type")
+
+ l_reason = iec61850.ClientReport_getReasonForInclusion(self._client_report,
+ mms_value_index)
+ l_reason_str = iec61850.ReasonForInclusion_getValueAsString(l_reason)
+ print(f"\tReason for inclusion: {l_reason_str}")
+
+ except RuntimeError as l_error:
+ print(f"Runtime Error (in subscriber thread): {l_error}")
+ except AssertionError as l_error:
+ print(f"Assertion Error (in subscriber thread): {l_error}")
+ except Exception as l_exception:
+ print(f"Exception (in subscriber thread): {l_exception}")
+
+
+class PyRCBSubscriber:
+ '''Class representing a RCB subscriber in Python,
+ and that owns the RCB handler in Python for processing the received RCB'''
+
+ def __init__(self):
+ self._libiec61850_rcb_client = None
+ self._internal_rcb_handler = PyRCBHandler()
+ self._wrapped_rcb_subscriber = iec61850.RCBSubscriber() # generic RCB subscriber
+ self._connection = None # do not destroy it
+ self._libiec61850_error_code = iec61850.IED_ERROR_OK
+
+ def __del__(self):
+ if self._connection is not None:
+ self.destroy()
+
+
+ def subscribe(self, i_connection, i_report_control_block_ref):
+ '''Select the subscription parameters and create the RCB subscription'''
+
+ # preconditions
+ assert iec61850.IedConnection_getState(i_connection) == iec61850.IED_STATE_CONNECTED, \
+ "error: Not connected"
+ assert i_report_control_block_ref, "error: the reference of the ReportControlBlock is empty"
+ assert self._libiec61850_rcb_client is None, "error: the RCB client is already created"
+
+ self._connection = i_connection
+
+ # Like the usual RCB subscription with the C API:
+ # read data set
+ print(f"RCBSubscription: create subscription for: '{i_report_control_block_ref}'")
+ l_return_value = iec61850.pyWrap_IedConnection_getRCBValues(self._connection,
+ i_report_control_block_ref,
+ None)
+ if isinstance(l_return_value, int):
+ self._libiec61850_error_code = l_return_value
+ else:
+ [self._libiec61850_rcb_client, self._libiec61850_error_code] = l_return_value
+
+ if self._libiec61850_error_code != iec61850.IED_ERROR_OK:
+ raise RuntimeError('IEC61850 error')
+
+
+ # Specific instructions with Python:
+ # Initialize the generic and wrapped 'subscriber'
+ l_client_report_control_block_id = \
+ iec61850.ClientReportControlBlock_getRptId(self._libiec61850_rcb_client)
+ self._wrapped_rcb_subscriber.setIedConnection(self._connection)
+ self._wrapped_rcb_subscriber.setRcbReference(i_report_control_block_ref)
+ self._wrapped_rcb_subscriber.setRcbRptId(l_client_report_control_block_id)
+
+ # Specific instructions with Python:
+ # Connect the specific callback/handler
+ self._internal_rcb_handler.thisown = 0 # the following subscriber will be the owner of this handler
+ self._wrapped_rcb_subscriber.setEventHandler(self._internal_rcb_handler)
+
+
+ # Like the usual RCB subscription, same feature but with a specific Python object
+ # Install handler for reports
+ l_registering_status = self._wrapped_rcb_subscriber.subscribe()
+ assert l_registering_status is True, "Error: Failed to register the RCBSubscriber"
+
+ # Like the usual RCB subscription with the C API:
+ # Set trigger options and enable report
+ l_trigger_options = iec61850.TRG_OPT_DATA_UPDATE | \
+ iec61850.TRG_OPT_INTEGRITY | \
+ iec61850.TRG_OPT_GI | \
+ iec61850.TRG_OPT_DATA_CHANGED | \
+ iec61850.TRG_OPT_QUALITY_CHANGED
+ l_rcb_attributes = iec61850.RCB_ELEMENT_RPT_ENA | iec61850.RCB_ELEMENT_TRG_OPS
+
+ iec61850.ClientReportControlBlock_setTrgOps(self._libiec61850_rcb_client,
+ l_trigger_options)
+
+ iec61850.ClientReportControlBlock_setRptEna(self._libiec61850_rcb_client, True)
+
+ self._libiec61850_error_code = \
+ iec61850.pyWrap_IedConnection_setRCBValues(self._connection,
+ self._libiec61850_rcb_client,
+ l_rcb_attributes,
+ True)
+
+ # Check subscription status
+ if self._libiec61850_error_code != iec61850.IED_ERROR_OK:
+ raise RuntimeError('IEC61850 error')
+
+
+ def destroy(self):
+ '''Stop the RCB subscription and destroy the internal objects'''
+
+ if self._libiec61850_rcb_client and \
+ iec61850.IedConnection_getState(self._connection) == iec61850.IED_STATE_CONNECTED:
+ # Disable reporting
+ iec61850.ClientReportControlBlock_setRptEna(self._libiec61850_rcb_client, False)
+ self._libiec61850_error_code = \
+ iec61850.pyWrap_IedConnection_setRCBValues(self._connection,
+ self._libiec61850_rcb_client,
+ iec61850.RCB_ELEMENT_RPT_ENA,
+ True)
+ # Check the 'disable reporting' command result
+ if self._libiec61850_error_code != iec61850.IED_ERROR_OK:
+ raise RuntimeError('IEC61850 error')
+
+ # Destroy the libiec61850 objects
+ if self._libiec61850_rcb_client:
+ iec61850.ClientReportControlBlock_destroy(self._libiec61850_rcb_client)
+ self._libiec61850_rcb_client = None
+
+ # Destroy the RCB subscriber
+ if self._wrapped_rcb_subscriber:
+ del self._wrapped_rcb_subscriber
+ self._wrapped_rcb_subscriber = None
+
+
+# MAIN
+L_RCBREF_STATUS = "simpleIOGenericIO/LLN0.RP.EventsRCB01"
+L_RCBREF_MEASUREMENTS = "simpleIOGenericIO/LLN0.BR.Measurements01"
+
+HOSTNAME = "localhost"
+PORT = 102
+if len(sys.argv) > 1:
+ HOSTNAME = sys.argv[1]
+if len(sys.argv) > 2:
+ PORT = int(sys.argv[2])
+
+[error, con] = open_connection(HOSTNAME, PORT)
+
+if error == iec61850.IED_ERROR_OK:
+ try:
+ rcb_subscriber_1 = PyRCBSubscriber()
+ rcb_subscriber_1.subscribe(con, L_RCBREF_STATUS)
+
+ rcb_subscriber_2 = PyRCBSubscriber()
+ rcb_subscriber_2.subscribe(con, L_RCBREF_MEASUREMENTS)
+
+ time.sleep(3)
+
+ rcb_subscriber_1.destroy()
+ rcb_subscriber_2.destroy()
+
+ except RuntimeError as caught_exception:
+ print(f"exception: {caught_exception}")
+ except AssertionError as caught_exception:
+ print(f"exception: {caught_exception}")
+
+ close_connection(con)
+else:
+ print("Connection error")
+
+destroy_connection(con)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 65b3ca1a..50e6d4c5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,7 @@
if(WITH_MBEDTLS)
include_directories(
${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls
- ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/include
+ ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.28/include
)
endif(WITH_MBEDTLS)
diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c
index 41fd1192..abf9a04c 100644
--- a/src/common/string_utilities.c
+++ b/src/common/string_utilities.c
@@ -197,6 +197,9 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1)
{
char* res = dest;
+ if (maxBufferSize < 1)
+ return NULL;
+
if (dest == NULL)
res = (char*)GLOBAL_MALLOC(maxBufferSize);
diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c
index 11b172c2..ff5101cc 100644
--- a/src/goose/goose_receiver.c
+++ b/src/goose/goose_receiver.c
@@ -234,13 +234,22 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) {
int padding = buffer[bufPos];
- int bitStringLength = (8 * (elementLength - 1)) - padding;
- if (bitStringLength == value->value.bitString.size) {
- memcpy(value->value.bitString.buf, buffer + bufPos + 1,
- elementLength - 1);
+
+ if (padding > 7) {
+ if (DEBUG_GOOSE_SUBSCRIBER)
+ printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n");
+
+ pe = GOOSE_PARSE_ERROR_INVALID_PADDING;
}
else {
- pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH;
+ int bitStringLength = (8 * (elementLength - 1)) - padding;
+ if (bitStringLength == value->value.bitString.size) {
+ memcpy(value->value.bitString.buf, buffer + bufPos + 1,
+ elementLength - 1);
+ }
+ else {
+ pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH;
+ }
}
}
else {
@@ -376,7 +385,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break;
}
- if ( pe != GOOSE_PARSE_ERROR_NO_ERROR ) {
+ if (pe != GOOSE_PARSE_ERROR_NO_ERROR) {
break; /* from while */
}
@@ -386,14 +395,16 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
}
if (elementIndex <= maxIndex) {
- pe = GOOSE_PARSE_ERROR_UNDERFLOW;
+ if (pe == GOOSE_PARSE_ERROR_NO_ERROR) {
+ pe = GOOSE_PARSE_ERROR_UNDERFLOW;
+ }
}
if (DEBUG_GOOSE_SUBSCRIBER) {
- switch ( pe ) {
+ switch (pe) {
case GOOSE_PARSE_ERROR_UNKNOWN_TAG:
printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag);
- break;
+ break;
case GOOSE_PARSE_ERROR_TAGDECODE:
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
break;
@@ -412,6 +423,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case GOOSE_PARSE_ERROR_LENGTH_MISMATCH:
printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n");
break;
+ case GOOSE_PARSE_ERROR_INVALID_PADDING:
+ printf("GOOSE_SUBSCRIBER: Malformed message: invalid padding!\n");
default:
break;
}
@@ -524,7 +537,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found boolean\n");
- value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
+
+ if (elementLength > 0) {
+ value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos));
+ }
+ else {
+ if (DEBUG_GOOSE_SUBSCRIBER)
+ printf("GOOSE_SUBSCRIBER: invalid length for boolean\n");
+
+ goto exit_with_error;
+ }
break;
diff --git a/src/goose/goose_subscriber.h b/src/goose/goose_subscriber.h
index 1c44158c..ca986633 100644
--- a/src/goose/goose_subscriber.h
+++ b/src/goose/goose_subscriber.h
@@ -47,6 +47,7 @@ typedef enum
GOOSE_PARSE_ERROR_UNDERFLOW,
GOOSE_PARSE_ERROR_TYPE_MISMATCH,
GOOSE_PARSE_ERROR_LENGTH_MISMATCH,
+ GOOSE_PARSE_ERROR_INVALID_PADDING
} GooseParseError;
typedef struct sGooseSubscriber* GooseSubscriber;
diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c
index caa07148..ee9ec9e9 100644
--- a/src/iec61850/client/client_control.c
+++ b/src/iec61850/client/client_control.c
@@ -29,7 +29,7 @@
#include "mms_client_connection.h"
#include "ied_connection_private.h"
-#if _MSC_VER
+#ifdef _MSC_VER
#define snprintf _snprintf
#endif
diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c
index fbaa74c2..af12d26a 100644
--- a/src/iec61850/client/client_report.c
+++ b/src/iec61850/client/client_report.c
@@ -139,7 +139,7 @@ ClientReport_getRptId(ClientReport self)
ReasonForInclusion
ClientReport_getReasonForInclusion(ClientReport self, int elementIndex)
{
- if (self->reasonForInclusion != NULL)
+ if ((self->reasonForInclusion != NULL) && (elementIndex < self->dataSetSize) && (elementIndex >= 0))
return self->reasonForInclusion[elementIndex];
else
return IEC61850_REASON_NOT_INCLUDED;
diff --git a/src/iec61850/client/client_report_control.c b/src/iec61850/client/client_report_control.c
index e126d570..6affd50e 100644
--- a/src/iec61850/client/client_report_control.c
+++ b/src/iec61850/client/client_report_control.c
@@ -808,8 +808,9 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD
handler(param->originalInvokeId, call->callbackParameter, err);
releaseWriteCall(self, call, param);
- }
+ goto exit_function;
+ }
param->currentItemId = LinkedList_getNext(param->currentItemId);
@@ -829,6 +830,7 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD
MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &writeError, param->domainId, itemId, value, writeVariableHandler, self);
if (writeError != MMS_ERROR_NONE) {
+
handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError));
releaseWriteCall(self, call, param);
@@ -839,6 +841,10 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD
if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n");
}
+
+exit_function:
+
+ return;
}
uint32_t
diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c
index 9ab48163..bc8e9ff6 100644
--- a/src/iec61850/client/ied_connection.c
+++ b/src/iec61850/client/ied_connection.c
@@ -105,6 +105,9 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError)
case MMS_ERROR_DEFINITION_OBJECT_UNDEFINED:
return IED_ERROR_OBJECT_UNDEFINED;
+ case MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE:
+ return IED_ERROR_TEMPORARILY_UNAVAILABLE;
+
default:
return IED_ERROR_UNKNOWN;
}
@@ -648,6 +651,15 @@ IedConnection_tick(IedConnection self)
return MmsConnection_tick(self->connection);
}
+void
+IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort)
+{
+ MmsConnection connection = self->connection;
+ IsoConnectionParameters isoP = MmsConnection_getIsoConnectionParameters(connection);
+
+ IsoConnectionParameters_setLocalTcpParameters(isoP, localIpAddress, localPort);
+}
+
void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
{
@@ -1745,7 +1757,7 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error
LinkedList_destroy(logicalDeviceNames);
}
else
- *error = iedConnection_mapMmsErrorToIedError(mmsError);
+ if (error) *error = iedConnection_mapMmsErrorToIedError(mmsError);
}
LinkedList /**/
@@ -3294,7 +3306,18 @@ uint32_t
IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements,
IedConnection_GenericServiceHandler handler, void* parameter)
{
- uint32_t invokeId = 0;
+ MmsError mmsError = MMS_ERROR_NONE;
+
+ IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
+
+ if (call == NULL) {
+ *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
+ goto exit_function;
+ }
+
+ call->callback = handler;
+ call->callbackParameter = parameter;
+ call->invokeId = 0;
char domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
@@ -3338,19 +3361,6 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons
isAssociationSpecific = true;
}
- MmsError mmsError;
-
- IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
-
- if (call == NULL) {
- *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED;
- goto exit_function;
- }
-
- call->callback = handler;
- call->callbackParameter = parameter;
- call->invokeId = 0;
-
LinkedList dataSetEntries = LinkedList_create();
LinkedList dataSetElement = LinkedList_getNext(dataSetElements);
@@ -3379,8 +3389,6 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons
&mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self);
}
- invokeId = call->invokeId;
-
*error = iedConnection_mapMmsErrorToIedError(mmsError);
cleanup_list:
@@ -3388,7 +3396,15 @@ cleanup_list:
LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy);
exit_function:
- return invokeId;
+
+ if (*error != IED_ERROR_OK) {
+ iedConnection_releaseOutstandingCall(self, call);
+
+ return 0;
+ }
+ else {
+ return call->invokeId;
+ }
}
LinkedList /* */
@@ -3513,6 +3529,8 @@ getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mms
if (handler)
handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable);
+
+ iedConnection_releaseOutstandingCall(self, call);
}
if (specs)
@@ -3523,7 +3541,7 @@ uint32_t
IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
IedConnection_GetDataSetDirectoryHandler handler, void* parameter)
{
- uint32_t invokeId = 0;
+ MmsError mmsError = MMS_ERROR_NONE;
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
@@ -3578,9 +3596,6 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error
isAssociationSpecific = true;
}
- MmsError mmsError = MMS_ERROR_NONE;
-
-
if (isAssociationSpecific)
MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self);
@@ -3590,7 +3605,15 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error
*error = iedConnection_mapMmsErrorToIedError(mmsError);
exit_function:
- return invokeId;
+
+ if (*error != IED_ERROR_OK) {
+ iedConnection_releaseOutstandingCall(self, call);
+
+ return 0;
+ }
+ else {
+ return call->invokeId;
+ }
}
ClientDataSet
diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h
index 852f2192..c3a174fe 100644
--- a/src/iec61850/inc/iec61850_client.h
+++ b/src/iec61850/inc/iec61850_client.h
@@ -231,6 +231,17 @@ IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig);
LIB61850_API void
IedConnection_destroy(IedConnection self);
+/**
+* \brief Set the local IP address and port to be used by the client
+*
+* NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use.
+*
+* \param self IedConnection instance
+* \param localIpAddress the local IP address or hostname as C string
+* \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use.
+*/
+LIB61850_API void
+IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort);
/**
* \brief set the connect timeout in ms
diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h
index c2765c45..9a364656 100644
--- a/src/iec61850/inc/iec61850_common.h
+++ b/src/iec61850/inc/iec61850_common.h
@@ -241,25 +241,6 @@ typedef enum {
* @{
*/
-#if (CONFIG_PROVIDE_OLD_FC_DEFINES == 1)
-#define ST IEC61850_FC_ST
-#define MX IEC61850_FC_MX
-#define SP IEC61850_FC_SP
-#define SV IEC61850_FC_SV
-#define CF IEC61850_FC_CF
-#define DC IEC61850_FC_DC
-#define SG IEC61850_FC_SG
-#define SE IEC61850_FC_SE
-#define SR IEC61850_FC_SR
-#define OR IEC61850_FC_OR
-#define BL IEC61850_FC_BL
-#define EX IEC61850_FC_EX
-#define CO IEC61850_FC_CO
-#define ALL IEC61850_FC_ALL
-#define NONE IEC61850_FC_NONE
-#endif /* (CONFIG_PROVIDE_OLD_FC_DEFINES == 1) */
-
-
/** FCs (Functional constraints) according to IEC 61850-7-2 */
typedef enum eFunctionalConstraint {
/** Status information */
diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h
index 32f6ff04..693dc7f4 100644
--- a/src/iec61850/inc/iec61850_dynamic_model.h
+++ b/src/iec61850/inc/iec61850_dynamic_model.h
@@ -395,6 +395,12 @@ LIB61850_API LogControlBlock*
LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps,
uint32_t intgPd, bool logEna, bool reasonCode);
+LIB61850_API const char*
+LogControlBlock_getName(LogControlBlock* self);
+
+LIB61850_API LogicalNode*
+LogControlBlock_getParent(LogControlBlock* self);
+
/**
* \brief create a log (used by the IEC 61850 log service)
*
diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h
index 750f47ac..a7ea685e 100644
--- a/src/iec61850/inc/iec61850_model.h
+++ b/src/iec61850/inc/iec61850_model.h
@@ -121,44 +121,6 @@ typedef enum {
IEC61850_CURRENCY = 30,
IEC61850_OPTFLDS = 31, /* bit-string(10) */
IEC61850_TRGOPS = 32 /* bit-string(6) */
-
-
-#if (CONFIG_IEC61850_USE_COMPAT_TYPE_DECLARATIONS == 1)
- ,
- BOOLEAN = 0,/* int */
- INT8 = 1, /* int8_t */
- INT16 = 2, /* int16_t */
- INT32 = 3, /* int32_t */
- INT64 = 4, /* int64_t */
- INT128 = 5, /* no native mapping! */
- INT8U = 6, /* uint8_t */
- INT16U = 7, /* uint16_t */
- INT24U = 8, /* uint32_t */
- INT32U = 9, /* uint32_t */
- FLOAT32 = 10, /* float */
- FLOAT64 = 11, /* double */
- ENUMERATED = 12,
- OCTET_STRING_64 = 13,
- OCTET_STRING_6 = 14,
- OCTET_STRING_8 = 15,
- VISIBLE_STRING_32 = 16,
- VISIBLE_STRING_64 = 17,
- VISIBLE_STRING_65 = 18,
- VISIBLE_STRING_129 = 19,
- VISIBLE_STRING_255 = 20,
- UNICODE_STRING_255 = 21,
- TIMESTAMP = 22,
- QUALITY = 23,
- CHECK = 24,
- CODEDENUM = 25,
- GENERIC_BITSTRING = 26,
- CONSTRUCTED = 27,
- ENTRY_TIME = 28,
- PHYCOMADDR = 29,
- CURRENCY = 30
- OPTFLDS = 31,
- TRGOPS = 32
-#endif
} DataAttributeType;
typedef enum {
diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h
index b0097e3f..3db3235e 100644
--- a/src/iec61850/inc/iec61850_server.h
+++ b/src/iec61850/inc/iec61850_server.h
@@ -3,7 +3,7 @@
*
* IEC 61850 server API for libiec61850.
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -42,6 +42,13 @@ extern "C" {
#include "iso_connection_parameters.h"
#include "iec61850_config_file_parser.h"
+#define IEC61850_REPORTSETTINGS_RPT_ID 1
+#define IEC61850_REPORTSETTINGS_BUF_TIME 2
+#define IEC61850_REPORTSETTINGS_DATSET 4
+#define IEC61850_REPORTSETTINGS_TRG_OPS 8
+#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16
+#define IEC61850_REPORTSETTINGS_INTG_PD 32
+
/**
* \brief Configuration object to configure IEC 61850 stack features
*/
@@ -99,6 +106,9 @@ struct sIedServerConfig
/** integrity report start times will by synchronized with straight numbers (default: false) */
bool syncIntegrityReportTimes;
+
+ /** for each configurable ReportSetting there is a separate flag (default: Dyn = enable write for all) */
+ uint8_t reportSettingsWritable;
};
/**
@@ -388,6 +398,27 @@ IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable);
LIB61850_API bool
IedServerConfig_isLogServiceEnabled(IedServerConfig self);
+/**
+ * \brief Make a configurable report setting writeable or read-only
+ *
+ * \note Can be used to implement some of Services\ReportSettings options
+ *
+ * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
+ * \param[in] isDyn true, when setting is writable ("Dyn") or false, when read-only
+ */
+LIB61850_API void
+IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn);
+
+/**
+ * \brief Check if a configurable report setting is writable or read-only
+ *
+ * \param[in] setting one of IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD
+ *
+ * \return isDyn true, when setting is writable ("Dyn") or false, when read-only
+ */
+LIB61850_API bool
+IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting);
+
/**
* An opaque handle for an IED server instance
*/
@@ -687,6 +718,21 @@ IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcb
LIB61850_API void
IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag);
+/**
+ * \brief Set the time quality for all timestamps internally generated by this IedServer instance
+ *
+ * You can call this function during the initialization of the server or whenever a time quality
+ * flag has to be updated (on clock failure or change of time synchronization state).
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param leapSecondKnown set/unset leap seconds known flag
+ * \param clockFailure set/unset clock failure flag
+ * \param clockNotSynchronized set/unset clock not synchronized flag
+ * \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)
+ */
+LIB61850_API void
+IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision);
+
/**@}*/
/**
@@ -710,8 +756,6 @@ IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName,
LIB61850_API void
IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter);
-
-
/**
* \brief get the peer address of this connection as string
*
@@ -770,6 +814,14 @@ typedef void (*IedConnectionIndicationHandler) (IedServer self, ClientConnection
LIB61850_API void
IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter);
+/**
+ * \brief Ignore all requests from clients
+ *
+ * \param self the instance of IedServer to configure.
+ * \param enable when true all requests from clients will be ignored
+ */
+void
+IedServer_ignoreClientRequests(IedServer self, bool enable);
/**@}*/
@@ -1344,6 +1396,26 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize);
LIB61850_API int
ControlAction_getCtlNum(ControlAction self);
+/**
+ * \brief Gets the synchroCheck bit provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the synchroCheck bit
+ */
+LIB61850_API bool
+ControlAction_getSynchroCheck(ControlAction self);
+
+/**
+ * \brief Gets the interlockCheck bit provided by the client
+ *
+ * \param self the control action instance
+ *
+ * \return the interlockCheck bit
+ */
+LIB61850_API bool
+ControlAction_getInterlockCheck(ControlAction self);
+
/**
* \brief Check if the control callback is called by a select or operate command
*
@@ -1820,6 +1892,121 @@ typedef MmsDataAccessError
LIB61850_API void
IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter);
+/**
+ * \brief Callback that is called in case of RCB access to give the user the opportunity to block or allow the operation
+ *
+ * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE
+ *
+ * \param parameter user provided parameter
+ * \param rcb affected report control block
+ * \param connection client connection that is involved
+ * \param operation one of the following operation event types: RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER
+ */
+typedef bool
+(*IedServer_RCBAccessHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation);
+
+/**
+ * \brief Set a handler to control read and write access to report control blocks (RCBs)
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param handler the event handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter);
+
+typedef enum {
+ LCB_EVENT_GET_PARAMETER,
+ LCB_EVENT_SET_PARAMETER
+} IedServer_LCBEventType;
+
+/**
+ * \brief Callback that is called in case of LCB access to give the user the opportunity to block or allow the operation
+ *
+ *
+ * \param parameter user provided parameter
+ * \param lcb affected log control block
+ * \param connection client connection that is involved
+ * \param operation one of the following operation event types: LCB_EVENT_GET_PARAMETER, LCB_EVENT_SET_PARAMETER
+ */
+typedef bool
+(*IedServer_LCBAccessHandler) (void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation);
+
+/**
+ * \brief Set a handler to control read and write access to log control blocks (LCBs)
+ *
+ * \param self the instance of IedServer to operate on.
+ * \param handler the event handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter);
+
+/**
+ * \brief Callback that is called when the client is trying to read log data
+ *
+ * \param parameter user provided parameter
+ * \param logRef object reference of the log
+ * \param connection client connection that is involved
+ *
+ * \return true to allow read log data, false to deny
+ */
+typedef bool
+(*IedServer_LogAccessHandler) (void* parameter, const char* logRef, ClientConnection connection);
+
+/**
+ * \brief Set a handler control access to a log (read log data)
+ *
+ * \param handler the callback handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter);
+
+typedef enum {
+ DATASET_CREATE,
+ DATASET_DELETE,
+ DATASET_READ,
+ DATASET_WRITE,
+ DATASET_GET_DIRECTORY
+} IedServer_DataSetOperation;
+
+/**
+ * \brief Callback that is called when the client is calling a dataset operation (create, delete, read, write, list directory)
+ *
+ * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE
+ *
+ * \param parameter user provided parameter
+ * \param connection client connection that is involved
+ * \param operation one of the following operation types: DATASET_CREATE, DATASET_DELETE, DATASET_READ, DATASET_WRITE, DATASET_GET_DIRECTORY
+ *
+ * \return true to allow operation, false to deny operation
+ */
+typedef bool
+(*IedServer_DataSetAccessHandler) (void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef);
+
+/**
+ * \brief Set a handler to control access to a dataset (create, delete, read, write, list directory)
+ *
+ * \param handler the callback handler to be used
+ * \param parameter a user provided parameter that is passed to the handler.
+ */
+LIB61850_API void
+IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter);
+
+typedef enum {
+ DIRECTORY_CAT_LD_LIST,
+ DIRECTORY_CAT_DATA_LIST,
+ DIRECTORY_CAT_DATASET_LIST,
+ DIRECTORY_CAT_LOG_LIST
+} IedServer_DirectoryCategory;
+
+typedef bool
+(*IedServer_DirectoryAccessHandler) (void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice);
+
+LIB61850_API void
+IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter);
+
/**@}*/
/**@}*/
diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h
index 14a694b1..d5555659 100644
--- a/src/iec61850/inc_private/ied_server_private.h
+++ b/src/iec61850/inc_private/ied_server_private.h
@@ -3,7 +3,7 @@
*
* Library private function definitions for IedServer.
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -50,6 +50,7 @@ struct sIedServer
bool enableBRCBResvTms;
bool enableOwnerForRCB;
bool syncIntegrityReportTimes;
+ uint8_t rcbSettingsWritable;
#endif
#if (CONFIG_MMS_THREADLESS_STACK != 1)
@@ -78,6 +79,8 @@ struct sIedServer
uint8_t edition;
+ uint8_t timeQuality; /* user settable time quality for internally updated times */
+
bool running;
};
diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h
index 7684739a..82ef712b 100644
--- a/src/iec61850/inc_private/logging.h
+++ b/src/iec61850/inc_private/logging.h
@@ -121,7 +121,7 @@ LIB61850_INTERNAL void
Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs);
LIB61850_INTERNAL MmsValue*
-LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig);
+LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection);
LIB61850_INTERNAL MmsDataAccessError
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,
diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h
index 3e896495..51714f11 100644
--- a/src/iec61850/inc_private/mms_mapping_internal.h
+++ b/src/iec61850/inc_private/mms_mapping_internal.h
@@ -325,7 +325,10 @@ struct sMmsMapping {
/* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked;
+
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore isModelLockedMutex;
+#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
IedServer iedServer;
@@ -334,6 +337,21 @@ struct sMmsMapping {
IedServer_RCBEventHandler rcbEventHandler;
void* rcbEventHandlerParameter;
+
+ IedServer_RCBAccessHandler rcbAccessHandler;
+ void* rcbAccessHandlerParameter;
+
+ IedServer_LCBAccessHandler lcbAccessHandler;
+ void* lcbAccessHandlerParameter;
+
+ IedServer_LogAccessHandler logAccessHandler;
+ void* logAccessHandlerParameter;
+
+ IedServer_DataSetAccessHandler dataSetAccessHandler;
+ void* dataSetAccessHandlerParameter;
+
+ IedServer_DirectoryAccessHandler directoryAccessHandler;
+ void* directoryAccessHandlerParameter;
};
#endif /* MMS_MAPPING_INTERNAL_H_ */
diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h
index a4e3433f..558343c2 100644
--- a/src/iec61850/inc_private/reporting.h
+++ b/src/iec61850/inc_private/reporting.h
@@ -140,7 +140,7 @@ LIB61850_INTERNAL MmsDataAccessError
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value,
MmsServerConnection connection);
-LIB61850_INTERNAL void
+LIB61850_INTERNAL bool
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName);
LIB61850_INTERNAL void
diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c
index 70527812..27d40624 100644
--- a/src/iec61850/server/impl/ied_server.c
+++ b/src/iec61850/server/impl/ied_server.c
@@ -1,7 +1,7 @@
/*
* ied_server.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -580,12 +580,19 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
+ self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable;
}
else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
+ self->rcbSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID +
+ IEC61850_REPORTSETTINGS_BUF_TIME +
+ IEC61850_REPORTSETTINGS_DATSET +
+ IEC61850_REPORTSETTINGS_TRG_OPS +
+ IEC61850_REPORTSETTINGS_OPT_FIELDS +
+ IEC61850_REPORTSETTINGS_INTG_PD;
#if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true;
#else
@@ -659,6 +666,7 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
}
#endif
+ IedServer_setTimeQuality(self, true, false, false, 10);
}
else {
IedServer_destroy(self);
@@ -688,6 +696,27 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler,
self->mmsMapping->rcbEventHandlerParameter = parameter;
}
+void
+IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter)
+{
+ self->mmsMapping->rcbAccessHandler = handler;
+ self->mmsMapping->rcbAccessHandlerParameter = parameter;
+}
+
+void
+IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter)
+{
+ self->mmsMapping->lcbAccessHandler = handler;
+ self->mmsMapping->lcbAccessHandlerParameter = parameter;
+}
+
+void
+IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter)
+{
+ self->mmsMapping->logAccessHandler = handler;
+ self->mmsMapping->logAccessHandlerParameter = parameter;
+}
+
void
IedServer_destroy(IedServer self)
{
@@ -910,11 +939,15 @@ IedServer_lockDataModel(IedServer self)
{
MmsServer_lockModel(self->mmsServer);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
+#endif
self->mmsMapping->isModelLocked = true;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex);
+#endif
}
void
@@ -928,13 +961,17 @@ IedServer_unlockDataModel(IedServer self)
/* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex);
+#endif
MmsServer_unlockModel(self->mmsServer);
self->mmsMapping->isModelLocked = false;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex);
+#endif
}
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
@@ -1479,7 +1516,7 @@ IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribu
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->dataModelLock);
#endif
- MmsValue_setUtcTimeMs(dataAttribute->mmsValue, value);
+ MmsValue_setUtcTimeMsEx(dataAttribute->mmsValue, value, self->timeQuality);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->dataModelLock);
#endif
@@ -1882,7 +1919,6 @@ private_IedServer_removeClientConnection(IedServer self, ClientConnection client
#endif
}
-
void
IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId)
{
@@ -1890,3 +1926,44 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId)
self->mmsMapping->gooseInterfaceId = StringUtils_copyString(interfaceId);
#endif
}
+
+void
+IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision)
+{
+ uint8_t timeQuality = 0;
+
+ if (clockNotSynchronized)
+ timeQuality += 0x20;
+
+ if (clockFailure)
+ timeQuality += 0x40;
+
+ if (leapSecondKnown)
+ timeQuality += 0x80;
+
+ timeQuality += (subsecondPrecision & 0x1f);
+
+ self->timeQuality = timeQuality;
+}
+
+void
+IedServer_ignoreClientRequests(IedServer self, bool enable)
+{
+ if (self->mmsServer) {
+ MmsServer_ignoreClientRequests(self->mmsServer, enable);
+ }
+}
+
+void
+IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter)
+{
+ self->mmsMapping->dataSetAccessHandler = handler;
+ self->mmsMapping->dataSetAccessHandlerParameter = parameter;
+}
+
+void
+IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter)
+{
+ self->mmsMapping->directoryAccessHandler = handler;
+ self->mmsMapping->directoryAccessHandlerParameter = parameter;
+}
diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c
index fdc38f8e..673f58b1 100644
--- a/src/iec61850/server/impl/ied_server_config.c
+++ b/src/iec61850/server/impl/ied_server_config.c
@@ -1,7 +1,7 @@
/*
* ied_server_config.c
*
- * Copyright 2018-2022 Michael Zillgith
+ * Copyright 2018-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -59,6 +59,12 @@ IedServerConfig_create()
self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = false;
+ self->reportSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID +
+ IEC61850_REPORTSETTINGS_BUF_TIME +
+ IEC61850_REPORTSETTINGS_DATSET +
+ IEC61850_REPORTSETTINGS_TRG_OPS +
+ IEC61850_REPORTSETTINGS_OPT_FIELDS +
+ IEC61850_REPORTSETTINGS_INTG_PD;
}
return self;
@@ -264,3 +270,34 @@ IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self)
{
return self->syncIntegrityReportTimes;
}
+
+static void
+configureSetting(IedServerConfig self, uint8_t flags, uint8_t setting, bool value)
+{
+ if (flags & setting)
+ {
+ if (value) {
+ self->reportSettingsWritable |= setting;
+ }
+ else {
+ self->reportSettingsWritable &= ~setting;
+ }
+ }
+}
+
+void
+IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn)
+{
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_RPT_ID, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_BUF_TIME, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_DATSET, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_TRG_OPS, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_OPT_FIELDS, isDyn);
+ configureSetting(self, setting, IEC61850_REPORTSETTINGS_INTG_PD, isDyn);
+}
+
+bool
+IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting)
+{
+ return (self->reportSettingsWritable & setting);
+}
diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c
index dc2ae540..de2b6f89 100644
--- a/src/iec61850/server/mms_mapping/control.c
+++ b/src/iec61850/server/mms_mapping/control.c
@@ -258,7 +258,7 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject
MmsValue_update(trkInst->ctlNum->mmsValue, controlObject->ctlNum);
if (trkInst->operTm)
- MmsValue_setUtcTimeMs(trkInst->operTm->mmsValue, controlObject->operateTime);
+ MmsValue_setUtcTimeMsEx(trkInst->operTm->mmsValue, controlObject->operateTime, self->iedServer->timeQuality);
if (trkInst->respAddCause)
MmsValue_update(trkInst->respAddCause->mmsValue, controlObject->addCause);
@@ -394,7 +394,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t)
- MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue, errVal);
@@ -590,9 +590,7 @@ setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs)
if (self->tOpOk) {
MmsValue* timestamp = self->tOpOk->mmsValue;
- MmsValue_setUtcTimeMs(timestamp, currentTimeInMs);
-
- /* TODO update time quality */
+ MmsValue_setUtcTimeMsEx(timestamp, currentTimeInMs, self->iedServer->timeQuality);
}
self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE;
@@ -862,6 +860,7 @@ executeStateMachine:
MmsValue* operTm = getOperParameterOperTime(controlObject->oper);
MmsValue_setUtcTime(operTm, 0);
+ MmsValue_setUtcTimeQuality(operTm, self->iedServer->timeQuality);
}
else {
MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId,
@@ -1660,8 +1659,6 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, &_varSpecList, &_values, false);
} /* ControlObject_sendCommandTerminationNegative() */
-
-
void
ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error,
ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode)
@@ -1875,7 +1872,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
if (DEBUG_IED_SERVER)
printf("IED_SERVER: select not applicable for control model %u\n", controlObject->ctlModel);
- value = controlObject->sbo;
+ value = &emptyString;
}
}
@@ -2008,7 +2005,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
}
if (controlObject->ctlModel == CONTROL_MODEL_STATUS_ONLY) {
- indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto free_and_return;
}
@@ -2035,7 +2032,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
MmsValue* test = getOperParameterTest(value);
if (checkValidityOfOriginParameter(origin) == false) {
- indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
@@ -2142,7 +2139,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
}
}
else {
- indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
}
}
else {
@@ -2163,12 +2160,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL)
|| (timeParameter == NULL)) {
- indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto free_and_return;
}
if (checkValidityOfOriginParameter(origin) == false) {
- indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
@@ -2224,7 +2221,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
(controlObject->testMode == testCondition)
) == false)
{
- indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
@@ -2372,7 +2369,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
MmsValue* origin = getCancelParameterOrigin(value);
if ((ctlNum == NULL) || (origin == NULL)) {
- indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
if (DEBUG_IED_SERVER)
printf("IED_SERVER: Invalid cancel message!\n");
goto free_and_return;
@@ -2501,6 +2498,22 @@ ControlAction_getCtlNum(ControlAction self)
return -1;
}
+bool
+ControlAction_getSynchroCheck(ControlAction self)
+{
+ ControlObject* controlObject = (ControlObject*) self;
+
+ return (bool)(controlObject->synchroCheck);
+}
+
+bool
+ControlAction_getInterlockCheck(ControlAction self)
+{
+ ControlObject* controlObject = (ControlObject*) self;
+
+ return (bool)(controlObject->interlockCheck);
+}
+
bool
ControlAction_isSelect(ControlAction self)
{
diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c
index 6fc8f7b4..c55a1ff4 100644
--- a/src/iec61850/server/mms_mapping/logging.c
+++ b/src/iec61850/server/mms_mapping/logging.c
@@ -1,7 +1,7 @@
/*
* logging.c
*
- * Copyright 2016-2022 Michael Zillgith
+ * Copyright 2016-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -43,6 +43,8 @@
#if (CONFIG_IEC61850_LOG_SERVICE == 1)
+static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}};
+
LogInstance*
LogInstance_create(LogicalNode* parentLN, const char* name)
{
@@ -418,7 +420,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, LogControl* logControl, IEC6
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t)
- MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue,
@@ -520,6 +522,19 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma
if (logControl == NULL) {
return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
}
+ else
+ {
+ if (self->lcbAccessHandler)
+ {
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
+
+ if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_SET_PARAMETER) == false) {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+
+ goto exit_function;
+ }
+ }
+ }
if (strcmp(varName, "LogEna") == 0) {
bool logEna = MmsValue_getBoolean(value);
@@ -699,7 +714,7 @@ exit_function:
}
MmsValue*
-LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig)
+LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection)
{
MmsValue* value = NULL;
@@ -723,27 +738,41 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain,
char* varName = MmsMapping_getNextNameElement(objectName);
- if (varName != NULL)
+ if (varName)
*(varName - 1) = 0;
LogControl* logControl = lookupLogControl(self, domain, lnName, objectName);
- if (logControl != NULL) {
+ if (logControl)
+ {
+ bool allowAccess = true;
+
+ if (self->lcbAccessHandler)
+ {
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
- updateLogStatusInLCB(logControl);
+ if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_GET_PARAMETER) == false) {
+ allowAccess = false;
- if (varName != NULL) {
- value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName);
+ value = &objectAccessDenied;
+ }
}
- else {
- value = logControl->mmsValue;
+
+ if (allowAccess) {
+ updateLogStatusInLCB(logControl);
+
+ if (varName) {
+ value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName);
+ }
+ else {
+ value = logControl->mmsValue;
+ }
}
}
return value;
}
-
static char*
createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl)
{
diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c
index a5352b00..5aab2585 100644
--- a/src/iec61850/server/mms_mapping/mms_goose.c
+++ b/src/iec61850/server/mms_mapping/mms_goose.c
@@ -1,7 +1,7 @@
/*
* mms_goose.c
*
- * Copyright 2013-2021 Michael Zillgith
+ * Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -231,7 +231,7 @@ updateGenericTrackingObjectValues(MmsGooseControlBlock gc, IEC61850_ServiceType
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t)
- MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), gc->mmsMapping->iedServer->timeQuality);
if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue,
diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c
index 3247d4b1..2d76f39c 100644
--- a/src/iec61850/server/mms_mapping/mms_mapping.c
+++ b/src/iec61850/server/mms_mapping/mms_mapping.c
@@ -1,7 +1,7 @@
/*
* mms_mapping.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -25,6 +25,7 @@
#include "mms_mapping.h"
#include "mms_mapping_internal.h"
#include "mms_server_internal.h"
+#include "mms_value_internal.h"
#include "stack_config.h"
#include "mms_goose.h"
@@ -68,6 +69,8 @@ typedef struct
uint64_t reservationTimeout;
} SettingGroup;
+static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}};
+
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
bool
@@ -136,7 +139,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
sizeof(MmsVariableSpecification));
namedVariable = namedVariable->typeSpec.array.elementTypeSpec;
- if (attribute->firstChild && ((DataAttribute*)(attribute->firstChild))->type != IEC61850_CONSTRUCTED) {
+ if (attribute->type != IEC61850_CONSTRUCTED) {
isBasicArray = true;
}
}
@@ -157,7 +160,8 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
DataAttribute* subDataAttribute = (DataAttribute*) attribute->firstChild;
int i = 0;
- while (subDataAttribute != NULL) {
+ while (subDataAttribute)
+ {
namedVariable->typeSpec.structure.elements[i] =
createNamedVariableFromDataAttribute(subDataAttribute);
@@ -693,7 +697,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, SettingGroupControlBlock* sg
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t)
- MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue,
@@ -840,7 +844,7 @@ MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock*
MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4);
MmsValue_setUint8(actSg, sgcb->actSG);
- MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
copySGCBValuesToTrackingObject(self, sgcb);
@@ -2720,7 +2724,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4);
MmsValue_setUint8(actSg, sg->sgcb->actSG);
- MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality);
}
else
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
@@ -3112,7 +3116,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
#if (CONFIG_IEC61850_LOG_SERVICE == 1)
/* LOG control block - LG */
if (isLogControlBlock(separator)) {
- retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId);
+ retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId, connection);
goto exit_function;
}
#endif
@@ -3155,31 +3159,35 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
continue;
if (strlen(rc->name) == variableIdLen) {
- if (strncmp(variableId, rc->name, variableIdLen) == 0) {
-
+ if (strncmp(variableId, rc->name, variableIdLen) == 0)
+ {
char* elementName = MmsMapping_getNextNameElement(reportName);
- ReportControl_readAccess(rc, self, connection, elementName);
-
MmsValue* value = NULL;
+ if (ReportControl_readAccess(rc, self, connection, elementName))
+ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- Semaphore_wait(rc->rcbValuesLock);
+ Semaphore_wait(rc->rcbValuesLock);
#endif
- if (elementName != NULL)
- value = ReportControl_getRCBValue(rc, elementName);
- else
- value = rc->rcbValues;
+ if (elementName != NULL)
+ value = ReportControl_getRCBValue(rc, elementName);
+ else
+ value = rc->rcbValues;
- if (value) {
- value = MmsValue_clone(value);
- MmsValue_setDeletableRecursive(value);
- }
+ if (value) {
+ value = MmsValue_clone(value);
+ MmsValue_setDeletableRecursive(value);
+ }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
- Semaphore_post(rc->rcbValuesLock);
+ Semaphore_post(rc->rcbValuesLock);
#endif
+ }
+ else {
+ value = &objectAccessDenied;
+ }
retValue = value;
@@ -3221,6 +3229,46 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection)
}
#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */
+static bool
+mmsGetNameListHandler(void* parameter, MmsGetNameListType nameListType, MmsDomain* domain, MmsServerConnection connection)
+{
+ MmsMapping* self = (MmsMapping*) parameter;
+
+ bool allowAccess = true;
+
+ if (self->directoryAccessHandler) {
+
+ LogicalDevice* ld = NULL;
+
+ IedServer_DirectoryCategory category = DIRECTORY_CAT_DATA_LIST;
+
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
+
+ if (domain) {
+ ld = IedModel_getDevice(self->model, MmsDomain_getName(domain));
+
+ if (ld == NULL) {
+ if (DEBUG_IED_SERVER)
+ printf("IED_SERVER: mmsGetNameListHandler -> LD not found!\n");
+ }
+ }
+
+ /* convert type to category */
+ if (nameListType == MMS_GETNAMELIST_DATA)
+ category = DIRECTORY_CAT_DATA_LIST;
+ else if (nameListType == MMS_GETNAMELIST_DATASETS)
+ category = DIRECTORY_CAT_DATASET_LIST;
+ else if (nameListType == MMS_GETNAMELIST_DOMAINS)
+ category = DIRECTORY_CAT_LD_LIST;
+ else if (nameListType == MMS_GETNAMELIST_JOURNALS)
+ category = DIRECTORY_CAT_LOG_LIST;
+
+ allowAccess = self->directoryAccessHandler(self->directoryAccessHandlerParameter, clientConnection, category, ld);
+ }
+
+ return allowAccess;
+}
+
static void /* is called by MMS server layer and runs in the connection handling thread */
mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event)
{
@@ -3317,18 +3365,16 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
{
return DATA_ACCESS_ERROR_SUCCESS;
}
- else {
-
+ else
+ {
StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId);
LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str);
- if (ln != NULL) {
-
-
+ if (ln != NULL)
+ {
char* doStart = strchr(separator + 1, '$');
-
if (doStart != NULL) {
char* doEnd = strchr(doStart + 1, '$');
@@ -3361,6 +3407,13 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
}
}
}
+ else {
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer,
+ connection);
+
+ return self->readAccessHandler(ld, ln, NULL, fc, clientConnection,
+ self->readAccessHandlerParameter);
+ }
}
}
}
@@ -3373,21 +3426,58 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
return DATA_ACCESS_ERROR_SUCCESS;
}
+static bool
+checkDataSetAccess(MmsMapping* self, MmsServerConnection connection, MmsVariableListType listType, MmsDomain* domain, char* listName, IedServer_DataSetOperation operation)
+{
+ bool accessGranted = true;
+
+ if (self->dataSetAccessHandler) {
+
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
+
+ char dataSetRef[130];
+ dataSetRef[0] = 0;
+
+ if (listType == MMS_ASSOCIATION_SPECIFIC) {
+ dataSetRef[0] = '@';
+ StringUtils_copyStringToBuffer(dataSetRef + 1, listName);
+ }
+ else if (listType == MMS_VMD_SPECIFIC) {
+ StringUtils_copyStringToBuffer(dataSetRef, listName);
+ }
+ else if (listType == MMS_DOMAIN_SPECIFIC) {
+ StringUtils_appendString(dataSetRef, 129, domain->domainName);
+ StringUtils_appendString(dataSetRef, 129, "/");
+ StringUtils_appendString(dataSetRef, 129, listName);
+ }
+
+ accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, clientConnection, operation, dataSetRef);
+ }
+
+ return accessGranted;
+}
+
static MmsError
-variableListChangedHandler (void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain,
+variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection)
{
MmsError allow = MMS_ERROR_NONE;
- (void)connection;
+ MmsMapping* self = (MmsMapping*) parameter;
/* TODO add log message */
#if (DEBUG_IED_SERVER == 1)
- if (create)
+ if (accessType == MMS_VARLIST_CREATE)
printf("IED_SERVER: create data set ");
- else
+ else if (accessType == MMS_VARLIST_DELETE)
printf("IED_SERVER: delete data set ");
+ else if (accessType == MMS_VARLIST_READ)
+ printf("IED_SERVER: read data set ");
+ else if (accessType == MMS_VARLIST_WRITE)
+ printf("IED_SERVER: write data set ");
+ else if (accessType == MMS_VARLIST_READ)
+ printf("IED_SERVER: get directory of data set ");
switch (listType) {
case MMS_VMD_SPECIFIC:
@@ -3404,120 +3494,172 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li
printf("specific (name=%s)\n", listName);
#endif /* (DEBUG_IED_SERVER == 1) */
- MmsMapping* self = (MmsMapping*) parameter;
+ if (accessType == MMS_VARLIST_CREATE) {
- if (create) {
- if (listType == MMS_DOMAIN_SPECIFIC) {
- /* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */
+ if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_CREATE)) {
- allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT;
+ if (listType == MMS_DOMAIN_SPECIFIC) {
+ /* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */
- IedModel* model = self->model;
+ allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT;
- LogicalDevice* ld = IedModel_getDevice(model, domain->domainName);
+ IedModel* model = self->model;
- if (ld != NULL) {
+ LogicalDevice* ld = IedModel_getDevice(model, domain->domainName);
- char lnName[129];
+ if (ld != NULL) {
- char* separator = strchr(listName, '$');
+ char lnName[129];
- if (separator != NULL) {
- int lnNameLen = separator - listName;
+ char* separator = strchr(listName, '$');
- memcpy(lnName, listName, lnNameLen);
- lnName[lnNameLen] = 0;
+ if (separator != NULL) {
+ int lnNameLen = separator - listName;
- if (LogicalDevice_getLogicalNode(ld, lnName) != NULL)
- allow = MMS_ERROR_NONE;
- }
+ memcpy(lnName, listName, lnNameLen);
+ lnName[lnNameLen] = 0;
- }
+ if (LogicalDevice_getLogicalNode(ld, lnName) != NULL)
+ allow = MMS_ERROR_NONE;
+ }
+ }
+ }
+ }
+ else {
+ allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
}
}
- else {
- /* Check if data set is referenced in a report */
+ else if (accessType == MMS_VARLIST_DELETE) {
- LinkedList rcElement = self->reportControls;
+ if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_DELETE)) {
+ /* Check if data set is referenced in a report */
- while ((rcElement = LinkedList_getNext(rcElement)) != NULL) {
- ReportControl* rc = (ReportControl*) rcElement->data;
+ LinkedList rcElement = self->reportControls;
- if (rc->isDynamicDataSet) {
- if (rc->dataSet != NULL) {
+ while ((rcElement = LinkedList_getNext(rcElement)) != NULL) {
+ ReportControl* rc = (ReportControl*) rcElement->data;
- if (listType == MMS_DOMAIN_SPECIFIC) {
- if (rc->dataSet->logicalDeviceName != NULL) {
- if (strcmp(rc->dataSet->name, listName) == 0) {
- if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) {
- allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
- break;
+ if (rc->isDynamicDataSet) {
+ if (rc->dataSet != NULL) {
+
+ if (listType == MMS_DOMAIN_SPECIFIC) {
+ if (rc->dataSet->logicalDeviceName != NULL) {
+ if (strcmp(rc->dataSet->name, listName) == 0) {
+ if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) {
+ allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
+ break;
+ }
}
}
}
- }
- else if (listType == MMS_VMD_SPECIFIC) {
- if (rc->dataSet->logicalDeviceName == NULL) {
- if (strcmp(rc->dataSet->name, listName) == 0) {
- allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
- break;
+ else if (listType == MMS_VMD_SPECIFIC) {
+ if (rc->dataSet->logicalDeviceName == NULL) {
+ if (strcmp(rc->dataSet->name, listName) == 0) {
+ allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
+ break;
+ }
}
}
- }
- else if (listType == MMS_ASSOCIATION_SPECIFIC) {
- if (rc->dataSet->logicalDeviceName == NULL) {
- if (strcmp(rc->dataSet->name, listName) == 0) {
- allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
- break;
+ else if (listType == MMS_ASSOCIATION_SPECIFIC) {
+ if (rc->dataSet->logicalDeviceName == NULL) {
+ if (strcmp(rc->dataSet->name, listName) == 0) {
+ allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
+ break;
+ }
}
}
- }
+ }
}
}
- }
-
#if (CONFIG_IEC61850_LOG_SERVICE == 1)
- /* check if data set is referenced in a log control block*/
- LinkedList logElement = self->logControls;
+ /* check if data set is referenced in a log control block*/
+ LinkedList logElement = self->logControls;
- while ((logElement = LinkedList_getNext(logElement)) != NULL) {
- LogControl* lc = (LogControl*) logElement->data;
+ while ((logElement = LinkedList_getNext(logElement)) != NULL) {
+ LogControl* lc = (LogControl*) logElement->data;
- if (lc->isDynamicDataSet) {
- if (lc->dataSet != NULL) {
+ if (lc->isDynamicDataSet) {
+ if (lc->dataSet != NULL) {
- if (listType == MMS_DOMAIN_SPECIFIC) {
- if (lc->dataSet->logicalDeviceName != NULL) {
- if (strcmp(lc->dataSet->name, listName) == 0) {
- if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) {
- allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
- break;
+ if (listType == MMS_DOMAIN_SPECIFIC) {
+ if (lc->dataSet->logicalDeviceName != NULL) {
+ if (strcmp(lc->dataSet->name, listName) == 0) {
+ if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) {
+ allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
+ break;
+ }
}
}
}
- }
- else if (listType == MMS_VMD_SPECIFIC) {
- if (lc->dataSet->logicalDeviceName == NULL) {
- if (strcmp(lc->dataSet->name, listName) == 0) {
- allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
- break;
+ else if (listType == MMS_VMD_SPECIFIC) {
+ if (lc->dataSet->logicalDeviceName == NULL) {
+ if (strcmp(lc->dataSet->name, listName) == 0) {
+ allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
+ break;
+ }
}
}
- }
+ }
}
}
- }
#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */
+
+ }
+ else {
+ allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
+ }
+ }
+ else if (accessType == MMS_VARLIST_READ)
+ {
+ if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_READ) == false) {
+ allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
+ }
+ }
+ else if (accessType == MMS_VARLIST_WRITE)
+ {
+ if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_WRITE) == false) {
+ allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
+ }
+ }
+ else if (accessType == MMS_VARLIST_GET_DIRECTORY) {
+ if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_GET_DIRECTORY) == false) {
+ allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
+ }
}
return allow;
}
+#if (CONFIG_IEC61850_LOG_SERVICE == 1)
+static bool
+mmsReadJournalHandler(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection)
+{
+ bool allowAccess = true;
+
+ MmsMapping* self = (MmsMapping*)parameter;
+
+ if (self->logAccessHandler) {
+ char logReference[130];
+ logReference[0] = 0;
+
+ StringUtils_appendString(logReference, 130, MmsDomain_getName(domain));
+ StringUtils_appendString(logReference, 130, "/");
+ StringUtils_appendString(logReference, 130, logName);
+
+ ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
+
+ allowAccess = self->logAccessHandler(self->logAccessHandlerParameter, logReference, clientConnection);
+ }
+
+ return allowAccess;
+}
+#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */
+
void
MmsMapping_installHandlers(MmsMapping* self)
{
@@ -3525,7 +3667,12 @@ MmsMapping_installHandlers(MmsMapping* self)
MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self);
MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self);
MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self);
- MmsServer_installVariableListChangedHandler(self->mmsServer, variableListChangedHandler, (void*) self);
+ MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self);
+ MmsServer_installGetNameListHandler(self->mmsServer, mmsGetNameListHandler, (void*) self);
+
+#if (CONFIG_IEC61850_LOG_SERVICE == 1)
+ MmsServer_installReadJournalHandler(self->mmsServer, mmsReadJournalHandler, (void*) self);
+#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */
}
void
@@ -3716,7 +3863,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
{
LinkedList element = self->reportControls;
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
+#endif
bool modelLocked = self->isModelLocked;
@@ -3732,8 +3881,7 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
continue;
break;
case REPORT_CONTROL_VALUE_CHANGED:
- if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) &&
- ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0))
+ if ((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0)
continue;
break;
case REPORT_CONTROL_QUALITY_CHANGED:
@@ -3754,7 +3902,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
Reporting_processReportEventsAfterUnlock(self);
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
+#endif
}
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
@@ -3775,13 +3925,17 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
if (DataSet_isMemberValue(dataSet, value, NULL)) {
MmsGooseControlBlock_setStateChangePending(gcb);
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
+#endif
if (self->isModelLocked == false) {
MmsGooseControlBlock_publishNewState(gcb);
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
+#endif
}
}
}
diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c
index d75bf137..9cbbb4f2 100644
--- a/src/iec61850/server/mms_mapping/reporting.c
+++ b/src/iec61850/server/mms_mapping/reporting.c
@@ -1,7 +1,7 @@
/*
* reporting.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -579,7 +579,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t)
- MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs());
+ MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue,
@@ -673,6 +673,43 @@ createDataSetValuesShadowBuffer(ReportControl* rc)
}
}
+static bool
+checkIfClientHasAccessToDataSetEntries(MmsMapping* mapping, MmsServerConnection connection, MmsNamedVariableList mmsVariableList)
+{
+ bool accessAllowed = true;
+
+ if (connection) {
+
+ LinkedList entryElem = LinkedList_getNext(mmsVariableList->listOfVariables);
+
+ while (entryElem) {
+ MmsNamedVariableListEntry entry = (MmsNamedVariableListEntry)LinkedList_getData(entryElem);
+
+ MmsValue* entryValue = mmsServer_getValue(mapping->mmsServer, entry->domain, entry->variableName, connection, true);
+
+ if (entryValue) {
+
+ if (MmsValue_getType(entryValue) == MMS_DATA_ACCESS_ERROR) {
+ accessAllowed = false;
+ }
+
+ MmsValue_deleteConditional(entryValue);
+ }
+ else {
+ accessAllowed = false;
+ }
+
+ if (accessAllowed == false)
+ break;
+
+ entryElem = LinkedList_getNext(entryElem);
+ }
+
+ }
+
+ return accessAllowed;
+}
+
static bool
updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection)
{
@@ -680,6 +717,8 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
MmsValue* dataSetValue;
+ bool isUsedDataSetDynamic = rc->isDynamicDataSet;
+
if (newDatSet != NULL) {
if (strcmp(MmsValue_toString(newDatSet), "") == 0) {
success = true;
@@ -751,20 +790,32 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
}
}
- if (rc->isDynamicDataSet) {
- if (rc->dataSet && dataSetChanged) {
- deleteDataSetValuesShadowBuffer(rc);
- MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet);
- rc->isDynamicDataSet = false;
- rc->dataSet = NULL;
- }
- }
-
- if (dataSetValue && dataSetChanged) {
+ if (dataSetValue) {
const char* dataSetName = MmsValue_toString(dataSetValue);
DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName);
+ if (dataSet) {
+
+ char domainNameBuf[130];
+
+ MmsMapping_getMmsDomainFromObjectReference(dataSetName, domainNameBuf);
+
+ MmsDomain* dsDomain = MmsDevice_getDomain(mapping->mmsDevice, domainNameBuf);
+
+ if (dsDomain) {
+ MmsNamedVariableList namedVariableList = MmsDomain_getNamedVariableList(dsDomain, dataSet->name);
+
+ if (namedVariableList) {
+ if (checkIfClientHasAccessToDataSetEntries(mapping, connection, namedVariableList) == false) {
+ goto exit_function;
+ }
+ }
+
+ }
+
+ }
+
#if (MMS_DYNAMIC_DATA_SETS == 1)
if (dataSet == NULL) {
dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName);
@@ -779,19 +830,28 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
MmsNamedVariableList mmsVariableList
= MmsServerConnection_getNamedVariableList(connection, dataSetName + 1);
- if (mmsVariableList != NULL)
+ if (mmsVariableList) {
+ if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) {
+ goto exit_function;
+ }
+
dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList);
+ }
}
}
-
}
/* check for VMD specific data set */
else if (dataSetName[0] == '/') {
MmsNamedVariableList mmsVariableList = MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1);
- if (mmsVariableList != NULL)
+ if (mmsVariableList) {
+ if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) {
+ goto exit_function;
+ }
+
dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList);
+ }
}
}
@@ -810,11 +870,20 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
- if (dataSetChanged == true) {
+ if (rc->dataSet && rc->dataSet != dataSet)
+ dataSetChanged = true;
+
+ if (dataSetChanged) {
/* delete pending event and create buffer for new data set */
deleteDataSetValuesShadowBuffer(rc);
+ if (isUsedDataSetDynamic) {
+ if (rc->dataSet) {
+ MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet);
+ }
+ }
+
rc->dataSet = dataSet;
createDataSetValuesShadowBuffer(rc);
@@ -830,7 +899,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
GLOBAL_FREEMEM(rc->inclusionFlags);
rc->inclusionFlags = (uint8_t*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t));
-
}
success = true;
@@ -862,7 +930,6 @@ createDataSetReferenceForDefaultDataSet(ReportControlBlock* rcb, ReportControl*
return dataSetReference;
}
-
static MmsValue*
createOptFlds(ReportControlBlock* reportControlBlock)
{
@@ -1697,21 +1764,35 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc)
}
}
-void
+bool
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName)
{
- (void)elementName;
+ bool accessAllowed = true;
+ MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS;
/* check reservation timeout */
if (rc->buffered) {
checkReservationTimeout(mmsMapping, rc);
}
- if (mmsMapping->rcbEventHandler) {
- ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection);
+ ClientConnection clientConnection = NULL;
- mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, DATA_ACCESS_ERROR_SUCCESS);
+ if (mmsMapping->rcbAccessHandler || mmsMapping->rcbEventHandler) {
+ clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection);
}
+
+ if (mmsMapping->rcbAccessHandler) {
+ if (mmsMapping->rcbAccessHandler(mmsMapping->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER) == false) {
+ accessError = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ accessAllowed = false;
+ }
+ }
+
+ if (mmsMapping->rcbEventHandler) {
+ mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, accessError);
+ }
+
+ return accessAllowed;
}
static bool
@@ -1809,6 +1890,15 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection);
+ /* check if write access to RCB is allowed on this connection */
+ if (self->rcbAccessHandler) {
+ if (self->rcbAccessHandler(self->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER) == false) {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+
+ goto exit_function_only_tracking;
+ }
+ }
+
/* check reservation timeout for buffered RCBs */
if (rc->buffered) {
@@ -1923,6 +2013,9 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
if (updateReportDataset(self, rc, NULL, connection)) {
if (rc->reserved == false) {
+
+ rc->resvTms = RESV_TMS_IMPLICIT_VALUE;
+
reserveRcb(rc, connection);
if (self->rcbEventHandler) {
@@ -2102,6 +2195,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "DatSet") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_DATSET))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2149,6 +2248,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "IntgPd") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_INTG_PD))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2196,6 +2301,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "TrgOps") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_TRG_OPS))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2269,6 +2380,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
else if (strcmp(elementName, "BufTm") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_BUF_TIME))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2302,6 +2419,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
}
else if (strcmp(elementName, "RptID") == 0) {
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_RPT_ID))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
#endif
@@ -2409,6 +2532,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
goto exit_function;
}
+ else if (strcmp(elementName, "OptFlds") == 0) {
+
+ if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_OPT_FIELDS))
+ {
+ retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
+ goto exit_function;
+ }
+ }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock);
@@ -2459,7 +2590,6 @@ exit_function:
}
}
-
}
else if (rc->resvTms == -1) {
if (rc->reserved == false) {
@@ -2491,6 +2621,8 @@ exit_function:
ReportControl_unlockNotify(rc);
+exit_function_only_tracking:
+
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
if (rc->buffered)
updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal);
@@ -3339,6 +3471,8 @@ sendNextReportEntrySegment(ReportControl* self)
MmsValue* subSeqNum = self->subSeqVal;
+ int numberOfAddedElements = 0;
+
for (i = 0; i < self->dataSet->elementCount; i++) {
if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) {
@@ -3420,6 +3554,8 @@ sendNextReportEntrySegment(ReportControl* self)
MmsValue_setBitStringBit(self->inclusionField, i, true);
+ numberOfAddedElements++;
+
accessResultSize += elementSize;
estimatedSegmentSize += elementSize;
}
@@ -3460,11 +3596,16 @@ sendNextReportEntrySegment(ReportControl* self)
uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize);
uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize);
- if ((int) completeMessageSize > maxMmsPduSize) {
+ if (((int) completeMessageSize > maxMmsPduSize) || (numberOfAddedElements == 0)) {
if (DEBUG_IED_SERVER)
- printf("IED_SERVER: report message too large %u (max = %i) -> skip message!\n", completeMessageSize, maxMmsPduSize);
+ printf("IED_SERVER: MMS PDU size too small to encode report data (max PDU size = %i) -> skip message!\n", maxMmsPduSize);
- goto exit_function;
+ self->startIndexForNextSegment = 0;
+ segmented = false;
+ moreFollows = false;
+ sentSuccess = true;
+
+ goto exit_remove_report;
}
/* encode the report message */
@@ -3688,6 +3829,8 @@ sendNextReportEntrySegment(ReportControl* self)
self->startIndexForNextSegment = maxIndex;
}
+exit_remove_report:
+
if (segmented == false) {
assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next);
@@ -3880,7 +4023,9 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
{
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex);
+#endif
if (self->isModelLocked == false) {
@@ -3897,7 +4042,9 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
}
}
+#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex);
+#endif
}
/*
diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c
index 13967de6..122fa8bd 100644
--- a/src/iec61850/server/model/cdc.c
+++ b/src/iec61850/server/model/cdc.c
@@ -676,7 +676,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot
CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat);
- addCommonOperateElements(cancel, isTimeActivated, true);
+ addCommonOperateElements(cancel, isTimeActivated, false);
}
}
diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c
index 18933c46..790c9be3 100644
--- a/src/iec61850/server/model/dynamic_model.c
+++ b/src/iec61850/server/model/dynamic_model.c
@@ -347,6 +347,18 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe
return self;
}
+const char*
+LogControlBlock_getName(LogControlBlock* self)
+{
+ return self->name;
+}
+
+LogicalNode*
+LogControlBlock_getParent(LogControlBlock* self)
+{
+ return self->parent;
+}
+
static void
LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb)
{
@@ -676,7 +688,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
self->triggerOptions = triggerOptions;
self->sAddr = sAddr;
- if ((arrayElements > 0) && (type != IEC61850_CONSTRUCTED)) {
+ if (arrayElements > 0) {
int i;
for (i = 0; i < arrayElements; i++) {
@@ -691,7 +703,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
arrayElement->fc = fc;
arrayElement->firstChild = NULL;
arrayElement->mmsValue = NULL;
- arrayElement->parent = parent;
+ arrayElement->parent = (ModelNode*)self;
arrayElement->sibling = NULL;
arrayElement->triggerOptions = triggerOptions;
arrayElement->sAddr = sAddr;
diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c
index 9d660ba5..375d62f9 100644
--- a/src/iec61850/server/model/model.c
+++ b/src/iec61850/server/model/model.c
@@ -649,106 +649,110 @@ ModelNode_getChildCount(ModelNode* modelNode) {
ModelNode*
ModelNode_getChild(ModelNode* self, const char* name)
{
- /* check for element separator */
- const char* separator = strchr(name, '.');
+ /* check for element separator */
+ const char* separator = strchr(name, '.');
- /* allow first character to be "." */
- if (separator == name)
- name++;
+ /* allow first character to be "." */
+ if (separator == name)
+ name++;
- /* check for array separator */
- const char* arraySeparator = strchr(name, '(');
+ /* check for array separator */
+ const char* arraySeparator = strchr(name, '(');
- if (arraySeparator) {
+ if (arraySeparator) {
- const char* arraySeparator2 = strchr(arraySeparator, ')');
+ const char* arraySeparator2 = strchr(arraySeparator, ')');
- if (arraySeparator2) {
- int idx = (int) strtol(arraySeparator + 1, NULL, 10);
+ if (arraySeparator2) {
+ int idx = (int) strtol(arraySeparator + 1, NULL, 10);
- ModelNode* arrayNode = NULL;
+ ModelNode* arrayNode = NULL;
- if (name == arraySeparator) {
- arrayNode = ModelNode_getChildWithIdx(self, idx);
- }
- else {
- char nameCopy[65];
+ if (name == arraySeparator) {
+ arrayNode = ModelNode_getChildWithIdx(self, idx);
+ }
+ else {
+ char nameCopy[65];
- const char* pos = name;
+ const char* pos = name;
- int cpyIdx = 0;
+ int cpyIdx = 0;
- while (pos < arraySeparator) {
- nameCopy[cpyIdx] = *pos;
- cpyIdx++;
- pos++;
- }
+ while (pos < arraySeparator) {
+ nameCopy[cpyIdx] = *pos;
+ cpyIdx++;
+ pos++;
+ }
- nameCopy[cpyIdx] = 0;
+ nameCopy[cpyIdx] = 0;
- ModelNode* childNode = ModelNode_getChild(self, nameCopy);
+ ModelNode* childNode = ModelNode_getChild(self, nameCopy);
- if (childNode) {
- arrayNode = ModelNode_getChildWithIdx(childNode, idx);
- }
- else
- return NULL;
- }
+ if (childNode) {
+ arrayNode = ModelNode_getChildWithIdx(childNode, idx);
+ }
+ else
+ return NULL;
+ }
- if (arrayNode) {
+ if (arrayNode) {
- if (*(arraySeparator2 + 1) == 0) {
- return arrayNode;
- }
- else {
- if (*(arraySeparator2 + 1) == '.')
- return ModelNode_getChild(arrayNode, arraySeparator2 + 2);
- else
- return ModelNode_getChild(arrayNode, arraySeparator2 + 1);
- }
+ if (*(arraySeparator2 + 1) == 0) {
+ return arrayNode;
+ }
+ else {
+ if (*(arraySeparator2 + 1) == '.')
+ return ModelNode_getChild(arrayNode, arraySeparator2 + 2);
+ else
+ return ModelNode_getChild(arrayNode, arraySeparator2 + 1);
+ }
- }
- else
- return NULL;
+ }
+ else
+ return NULL;
+ }
+ else {
+ /* invalid name */
+ return NULL;
+ }
- }
- else {
- /* invalid name */
- return NULL;
- }
+ }
- }
+ int nameElementLength = 0;
- int nameElementLength = 0;
+ if (separator != NULL)
+ nameElementLength = (separator - name);
+ else
+ nameElementLength = strlen(name);
- if (separator != NULL)
- nameElementLength = (separator - name);
- else
- nameElementLength = strlen(name);
+ ModelNode* nextNode = self->firstChild;
- ModelNode* nextNode = self->firstChild;
+ ModelNode* matchingNode = NULL;
- ModelNode* matchingNode = NULL;
+ while (nextNode) {
- while (nextNode) {
- int nodeNameLen = strlen(nextNode->name);
+ if (nextNode->name == NULL) {
+ break; /* is an array element */
+ }
- if (nodeNameLen == nameElementLength) {
+ int nodeNameLen = strlen(nextNode->name);
- if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
- matchingNode = nextNode;
- break;
- }
- }
+ if (nodeNameLen == nameElementLength) {
- nextNode = nextNode->sibling;
- }
+ if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
+ matchingNode = nextNode;
+ break;
+ }
+ }
- if ((separator != NULL) && (matchingNode != NULL)) {
- return ModelNode_getChild(matchingNode, separator + 1);
- }
- else
- return matchingNode;
+ nextNode = nextNode->sibling;
+ }
+
+ if ((separator != NULL) && (matchingNode != NULL)) {
+ return ModelNode_getChild(matchingNode, separator + 1);
+ }
+ else
+ return matchingNode;
}
ModelNode*
diff --git a/src/mms/asn1/asn1_ber_primitive_value.c b/src/mms/asn1/asn1_ber_primitive_value.c
index 0dbd15bf..55cf626b 100644
--- a/src/mms/asn1/asn1_ber_primitive_value.c
+++ b/src/mms/asn1/asn1_ber_primitive_value.c
@@ -1,24 +1,24 @@
/*
* asn1_ber_primitive_value.c
*
- * Copyright 2013-2020 Michael Zillgith
+ * Copyright 2013-2022 Michael Zillgith
*
- * This file is part of libIEC61850.
+ * This file is part of libIEC61850.
*
- * libIEC61850 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * libIEC61850 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * libIEC61850 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.
+ * libIEC61850 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 libIEC61850. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with libIEC61850. If not, see .
*
- * See COPYING file for the complete license text.
+ * See COPYING file for the complete license text.
*/
#include "libiec61850_platform_includes.h"
diff --git a/src/mms/asn1/ber_decode.c b/src/mms/asn1/ber_decode.c
index 12078610..8a4866a8 100644
--- a/src/mms/asn1/ber_decode.c
+++ b/src/mms/asn1/ber_decode.c
@@ -1,7 +1,7 @@
/*
* ber_decoder.c
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
diff --git a/src/mms/asn1/ber_encoder.c b/src/mms/asn1/ber_encoder.c
index 63566f38..53a5d178 100644
--- a/src/mms/asn1/ber_encoder.c
+++ b/src/mms/asn1/ber_encoder.c
@@ -1,7 +1,7 @@
/*
* ber_encoder.c
*
- * Copyright 2013 Michael Zillgith
+ * Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -356,6 +356,28 @@ BerEncoder_UInt32determineEncodedSize(uint32_t value)
return size;
}
+int
+BerEncoder_Int32determineEncodedSize(int32_t value)
+{
+ uint8_t* valueArray = (uint8_t*) &value;
+ uint8_t valueBuffer[5];
+
+ valueBuffer[0] = 0;
+
+ int i;
+ for (i = 0; i < 4; i++) {
+ valueBuffer[i + 1] = valueArray[i];
+ }
+
+#if (ORDER_LITTLE_ENDIAN == 1)
+ BerEncoder_revertByteOrder(valueBuffer + 1, 4);
+#endif
+
+ int size = BerEncoder_compressInteger(valueBuffer, 5);
+
+ return size;
+}
+
int
BerEncoder_determineLengthSize(uint32_t length)
{
@@ -457,7 +479,6 @@ BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufL
requiredBytes--;
}
}
-
}
return encodedBytes;
diff --git a/src/mms/asn1/ber_integer.c b/src/mms/asn1/ber_integer.c
index 9a7e8030..01281e3f 100644
--- a/src/mms/asn1/ber_integer.c
+++ b/src/mms/asn1/ber_integer.c
@@ -1,7 +1,7 @@
/*
* ber_integer.c
*
- * Copyright 2013-2020 Michael Zillgith
+ * Copyright 2013-2022 Michael Zillgith
*
* This file is part of libIEC61850.
*
diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h
index cdd04bcb..df413579 100644
--- a/src/mms/inc/iso_connection_parameters.h
+++ b/src/mms/inc/iso_connection_parameters.h
@@ -1,7 +1,7 @@
/*
* iso_connection_parameters.h
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -24,6 +24,10 @@
#ifndef ISO_CONNECTION_PARAMETERS_H_
#define ISO_CONNECTION_PARAMETERS_H_
+#ifndef CONFIG_MMS_SUPPORT_TLS
+#define CONFIG_MMS_SUPPORT_TLS 0
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -145,6 +149,9 @@ struct sIsoConnectionParameters
const char* hostname;
int tcpPort;
+ const char* localIpAddress;
+ int localTcpPort;
+
uint8_t remoteApTitle[10];
int remoteApTitleLen;
int remoteAEQualifier;
@@ -215,6 +222,20 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s
LIB61850_API void
IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort);
+/**
+* \brief Set Local TCP parameters (FOR LIBRARY INTERNAL USE)
+*
+* NOTE: This function used internally by the MMS Client library. When using the MMS or IEC 61850 API
+* there should be no reason for the user to call this function
+*
+* \param self the IsoConnectionParameters instance
+* \param localIpAddress the hostname of local IP address of the server
+* \param localTcpPort the local TCP port number of the server
+*/
+LIB61850_API void
+IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort);
+
+
/**
* \brief set the remote AP-Title and AE-Qualifier
*
diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h
index 70bedc28..86f12c9b 100644
--- a/src/mms/inc/mms_client_connection.h
+++ b/src/mms/inc/mms_client_connection.h
@@ -648,6 +648,23 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms
MmsConnection_WriteVariableHandler handler, void* parameter);
+/**
+ * \brief Write a single variable to the server (using component alternate access)
+ *
+ * \param self MmsConnection instance to operate on
+ * \param mmsError user provided variable to store error code
+ * \param domainId the domain name of the variable to be written
+ * \param itemId name of the variable to be written
+ * \param componentId the name of the variable component
+ * \param value value of the variable to be written
+ *
+ * \return when successful, the data access error value returned by the server
+ */
+LIB61850_API MmsDataAccessError
+MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
+ const char* domainId, const char* itemId,
+ const char* componentId, MmsValue* value);
+
/**
* \brief Write a single array element with a component to an array type variable
*
@@ -672,6 +689,11 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint
uint32_t arrayIndex, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter);
+LIB61850_API void
+MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+ const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
+ MmsConnection_WriteVariableHandler handler, void* parameter);
+
/**
* \brief Write a single array element or a sub array to an array type variable
*
diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h
index 7e6118d0..15248789 100644
--- a/src/mms/inc/mms_server.h
+++ b/src/mms/inc/mms_server.h
@@ -1,7 +1,7 @@
/*
* mms_server.h
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -57,30 +57,70 @@ MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress);
LIB61850_INTERNAL bool
MmsServer_isRunning(MmsServer self);
+typedef enum {
+ MMS_VARLIST_CREATE,
+ MMS_VARLIST_DELETE,
+ MMS_VARLIST_READ,
+ MMS_VARLIST_WRITE,
+ MMS_VARLIST_GET_DIRECTORY
+} MmsVariableListAccessType;
+
/**
- * \brief callback handler that is called whenever a named variable list changes
+ * \brief callback handler that is called for each named variable list access
*
* \param parameter a user provided parameter
- * \param create if true the the request if a request to create a new variable list, false is a delete request
+ * \param accessType the kind of access (create, delete, read, write, get directory)
* \param listType the type (scope) of the named variable list (either domain, association or VMD specific)
* \param domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!)
* \param listName the name
- * \param connection client connection that requests the creation of deletion of the variable list
+ * \param connection client connection that is accessing the named variable list
*
* \return MMS_ERROR_NONE if the request is accepted, otherwise the MmsError value that has to be sent back to the client
*/
-typedef MmsError (*MmsNamedVariableListChangedHandler)(void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain,
+typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection);
/**
- * \brief Install callback handler that is called when a named variable list changes (is created or deleted)
+ * \brief Install callback handler that is called when a named variable list is accessed by a client
*
* \param self the MmsServer instance to operate on
* \param handler the callback handler function
* \param parameter user provided parameter that is passed to the callback handler
*/
LIB61850_INTERNAL void
-MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter);
+MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter);
+
+/**
+ * \brief callback handler that is called for each received read journal request
+ *
+ * \param parameter a user provided parameter
+ * \param domain the MMS domain the journal is belonging to
+ * \param logName the name of the journal
+ * \param connection client connection that is accessing the journal
+ */
+typedef bool (*MmsReadJournalHandler)(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection);
+
+/**
+ * \brief Install callback handler that is called when a journal is accessed by a client
+ *
+ * \param self the MmsServer instance to operate on
+ * \param handler the callback handler function
+ * \param parameter user provided parameter that is passed to the callback handler
+ */
+LIB61850_INTERNAL void
+MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter);
+
+typedef enum {
+ MMS_GETNAMELIST_DOMAINS,
+ MMS_GETNAMELIST_JOURNALS,
+ MMS_GETNAMELIST_DATASETS,
+ MMS_GETNAMELIST_DATA
+} MmsGetNameListType;
+
+typedef bool (*MmsGetNameListHandler)(void* parameter, MmsGetNameListType nameListType, MmsDomain* domain, MmsServerConnection connection);
+
+LIB61850_INTERNAL void
+MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter);
/**
* \brief ObtainFile service callback handler
@@ -370,6 +410,9 @@ MmsServerConnection_getLocalAddress(MmsServerConnection self);
LIB61850_INTERNAL void*
MmsServerConnection_getSecurityToken(MmsServerConnection self);
+LIB61850_INTERNAL void
+MmsServer_ignoreClientRequests(MmsServer self, bool enable);;
+
/**@}*/
#ifdef __cplusplus
diff --git a/src/mms/inc/mms_value.h b/src/mms/inc/mms_value.h
index e67bccb2..206345c4 100644
--- a/src/mms/inc/mms_value.h
+++ b/src/mms/inc/mms_value.h
@@ -498,12 +498,30 @@ MmsValue_getUtcTimeInMsWithUs(const MmsValue* self, uint32_t* usec);
* bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time)
*
* \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
- *
* \param timeQuality the byte representing the time quality
*/
LIB61850_API void
MmsValue_setUtcTimeQuality(MmsValue* self, uint8_t timeQuality);
+/**
+ * \brief Update an MmsValue object of type MMS_UTCTIME with a millisecond time.
+ *
+ * Meaning of the bits in the timeQuality byte:
+ *
+ * bit 7 = leapSecondsKnown
+ * bit 6 = clockFailure
+ * bit 5 = clockNotSynchronized
+ * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time)
+ *
+ * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
+ * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC)
+ * \param timeQuality the byte representing the time quality
+ *
+ * \return the updated MmsValue instance
+ */
+LIB61850_API MmsValue*
+MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality);
+
/**
* \brief get the TimeQuality byte of the UtcTime
*
diff --git a/src/mms/inc_private/cotp.h b/src/mms/inc_private/cotp.h
index b7877cc8..3cbe2681 100644
--- a/src/mms/inc_private/cotp.h
+++ b/src/mms/inc_private/cotp.h
@@ -114,4 +114,7 @@ CotpConnection_getRemoteRef(CotpConnection* self);
LIB61850_INTERNAL int
CotpConnection_getLocalRef(CotpConnection* self);
+LIB61850_INTERNAL void
+CotpConnection_flushBuffer(CotpConnection* self);
+
#endif /* COTP_H_ */
diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h
index 57243230..aa0e60f3 100644
--- a/src/mms/inc_private/mms_client_internal.h
+++ b/src/mms/inc_private/mms_client_internal.h
@@ -272,6 +272,11 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
int startIndex, int elementCount,
MmsValue* value, ByteBuffer* writeBuffer);
+LIB61850_INTERNAL int
+mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component,
+ MmsValue* value,
+ ByteBuffer* writeBuffer);
+
LIB61850_INTERNAL int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component,
diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h
index 780e1d74..ec70d359 100644
--- a/src/mms/inc_private/mms_server_internal.h
+++ b/src/mms/inc_private/mms_server_internal.h
@@ -123,8 +123,14 @@ struct sMmsServer {
MmsConnectionHandler connectionHandler;
void* connectionHandlerParameter;
- MmsNamedVariableListChangedHandler variableListChangedHandler; /* TODO this is only required if dynamic data sets are supported! */
- void* variableListChangedHandlerParameter;
+ MmsNamedVariableListAccessHandler variableListAccessHandler;
+ void* variableListAccessHandlerParameter;
+
+ MmsReadJournalHandler readJournalHandler;
+ void* readJournalHandlerParameter;
+
+ MmsGetNameListHandler getNameListHandler;
+ void* getNameListHandlerParameter;
AcseAuthenticator authenticator;
void* authenticatorParameter;
@@ -135,7 +141,8 @@ struct sMmsServer {
Map openConnections;
Map valueCaches;
- bool isLocked;
+
+ bool blockRequests;
ByteBuffer* transmitBuffer; /* global buffer for encoding reports, delayed responses... */
#if (CONFIG_MMS_THREADLESS_STACK != 1)
@@ -420,7 +427,7 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection,
uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults);
LIB61850_INTERNAL MmsError
-mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain,
+mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection);
#endif /* MMS_SERVER_INTERNAL_H_ */
diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h
index ac16e445..444989c9 100644
--- a/src/mms/inc_private/mms_server_libinternal.h
+++ b/src/mms/inc_private/mms_server_libinternal.h
@@ -1,7 +1,7 @@
/*
* mms_server_libinternal.h
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -196,7 +196,7 @@ MmsServer_getConnectionCounter(MmsServer self);
LIB61850_INTERNAL void
MmsServer_stopListeningThreadless(MmsServer self);
-LIB61850_INTERNAL const char*
+LIB61850_INTERNAL const char*
MmsServer_getFilesystemBasepath(MmsServer self);
#endif /* MMS_SERVER_LIBINTERNAL_H_ */
diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c
index cb58e98f..4f37176b 100644
--- a/src/mms/iso_client/iso_client_connection.c
+++ b/src/mms/iso_client/iso_client_connection.c
@@ -692,8 +692,13 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
/* set timeout for connect */
self->nextReadTimeout = Hal_getTimeInMs() + connectTimeoutInMs;
+ /* Connect to Local Ip Address*/
+ if (self->parameters->localIpAddress) {
+ Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort);
+ }
+
if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) {
-
+
Socket_destroy(self->socket);
self->socket = NULL;
@@ -704,9 +709,9 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
success = false;
}
-
+
Semaphore_post(self->tickMutex);
-
+
return success;
}
diff --git a/src/mms/iso_common/iso_connection_parameters.c b/src/mms/iso_common/iso_connection_parameters.c
index 3fdf32c9..aa5334b3 100644
--- a/src/mms/iso_common/iso_connection_parameters.c
+++ b/src/mms/iso_common/iso_connection_parameters.c
@@ -104,6 +104,18 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const cha
self->tcpPort = tcpPort;
}
+void
+IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort)
+{
+ if (self) {
+ if (localIpAddress) {
+ self->localIpAddress = strdup(localIpAddress);
+ self->localTcpPort = localTcpPort;
+ }
+ }
+}
+
+
void
IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier)
{
diff --git a/src/mms/iso_cotp/cotp.c b/src/mms/iso_cotp/cotp.c
index f57e05bc..2b4e43e6 100644
--- a/src/mms/iso_cotp/cotp.c
+++ b/src/mms/iso_cotp/cotp.c
@@ -5,7 +5,7 @@
*
* Partial implementation of the ISO 8073 COTP (ISO TP0) protocol for MMS.
*
- * Copyright 2013-2018 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -174,6 +174,38 @@ writeToSocket(CotpConnection* self, uint8_t* buf, int size)
#endif
}
+static bool
+flushBuffer(CotpConnection* self)
+{
+ if (self->socketExtensionBufferFill > 0) {
+
+ int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill);
+
+ if (sentBytes > 0) {
+
+ if (sentBytes != self->socketExtensionBufferFill) {
+ int target = 0;
+ int i;
+ uint8_t* buf = self->socketExtensionBuffer;
+
+ for (i = sentBytes; i < self->socketExtensionBufferFill; i++) {
+ buf[target++] = buf[i];
+ }
+
+ self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes;
+ }
+ else {
+ self->socketExtensionBufferFill = 0;
+ }
+ }
+ else if (sentBytes == -1) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static bool
sendBuffer(CotpConnection* self)
{
@@ -182,7 +214,15 @@ sendBuffer(CotpConnection* self)
bool retVal = false;
- int sentBytes = writeToSocket(self, buffer, remainingSize);
+ if (flushBuffer(self) == false) {
+ goto exit_function;
+ }
+
+ int sentBytes = 0;
+
+ if (self->socketExtensionBufferFill == 0) {
+ sentBytes = writeToSocket(self, buffer, remainingSize);
+ }
if (sentBytes == -1)
goto exit_function;
@@ -215,33 +255,6 @@ exit_function:
return retVal;
}
-static void
-flushBuffer(CotpConnection* self)
-{
- if (self->socketExtensionBufferFill > 0) {
-
- int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill);
-
- if (sentBytes > 0) {
-
- if (sentBytes != self->socketExtensionBufferFill) {
- int target = 0;
- int i;
- uint8_t* buf = self->socketExtensionBuffer;
-
- for (i = sentBytes; i < self->socketExtensionBufferFill; i++) {
- buf[target++] = buf[i];
- }
-
- self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes;
- }
- else {
- self->socketExtensionBufferFill = 0;
- }
- }
- }
-}
-
CotpIndication
CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
{
@@ -262,7 +275,9 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int totalSize = (fragments * (COTP_DATA_HEADER_SIZE + 4)) + payload->length;
/* try to flush extension buffer */
- flushBuffer(self);
+ if (flushBuffer(self) == false) {
+ return COTP_ERROR;
+ }
/* check if totalSize will fit in extension buffer */
if (self->socketExtensionBuffer) {
@@ -281,7 +296,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int currentChainIndex = 0;
if (DEBUG_COTP)
- printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
+ printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
uint8_t* buffer = self->writeBuffer->buffer;
@@ -307,7 +322,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
if (currentChainIndex >= currentChain->partLength) {
currentChain = currentChain->nextPart;
if (DEBUG_COTP)
- printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
+ printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength);
currentChainIndex = 0;
}
@@ -756,6 +771,13 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size)
#endif
}
+void
+CotpConnection_flushBuffer(CotpConnection* self)
+{
+ if (self->socketExtensionBufferFill > 0)
+ flushBuffer(self);
+}
+
TpktState
CotpConnection_readToTpktBuffer(CotpConnection* self)
{
@@ -765,6 +787,14 @@ CotpConnection_readToTpktBuffer(CotpConnection* self)
assert (bufferSize > 4);
+ if (self->socketExtensionBufferFill > 0) {
+ if (flushBuffer(self) == false)
+ goto exit_error;
+
+ if (self->socketExtensionBufferFill > 0)
+ goto exit_waiting;
+ }
+
int readBytes;
if (bufPos < 4) {
diff --git a/src/mms/iso_mms/asn1c/asn_internal.h b/src/mms/iso_mms/asn1c/asn_internal.h
index 493b4336..5d6910d8 100644
--- a/src/mms/iso_mms/asn1c/asn_internal.h
+++ b/src/mms/iso_mms/asn1c/asn_internal.h
@@ -11,6 +11,8 @@
#include "asn_application.h" /* Application-visible API */
+#define EMIT_ASN_DEBUG 0
+
#include "lib_memory.h"
#ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */
diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c
index de7db996..a9763c08 100644
--- a/src/mms/iso_mms/client/mms_client_connection.c
+++ b/src/mms/iso_mms/client/mms_client_connection.c
@@ -354,10 +354,6 @@ sendMessage(MmsConnection self, ByteBuffer* message)
}
#endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */
-#if (CONFIG_MMS_COLLECT_STATISTICS == 1)
- self->statAplMessagesSent++;
-#endif
-
IsoClientConnection_sendMessage(self->isoClient, message);
}
@@ -4383,6 +4379,69 @@ exit_function:
return;
}
+MmsDataAccessError
+MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError,
+ const char* domainId, const char* itemId,
+ const char* componentId, MmsValue* value)
+{
+ struct writeVariableParameters parameter;
+
+ MmsError err = MMS_ERROR_NONE;
+
+ parameter.waitForResponse = Semaphore_create(1);
+ parameter.err = MMS_ERROR_NONE;
+ parameter.accessError = DATA_ACCESS_ERROR_SUCCESS;
+
+ Semaphore_wait(parameter.waitForResponse);
+
+ MmsConnection_writeVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, value, writeVariableHandler, ¶meter);
+
+ if (err == MMS_ERROR_NONE) {
+ Semaphore_wait(parameter.waitForResponse);
+
+ err = parameter.err;
+ }
+
+ Semaphore_destroy(parameter.waitForResponse);
+
+ if (mmsError)
+ *mmsError = err;
+
+ return parameter.accessError;
+}
+
+void
+MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError,
+ const char* domainId, const char* itemId, const char* componentId, MmsValue* value,
+ MmsConnection_WriteVariableHandler handler, void* parameter)
+{
+ if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) {
+ if (mmsError)
+ *mmsError = MMS_ERROR_CONNECTION_LOST;
+ goto exit_function;
+ }
+
+ ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient);
+
+ uint32_t invokeId = getNextInvokeId(self);
+
+ if (usedInvokeId)
+ *usedInvokeId = invokeId;
+
+ mmsClient_createWriteRequestComponent(invokeId, domainId, itemId, componentId, value, payload);
+
+ MmsClientInternalParameter intParam;
+ intParam.ptr = NULL;
+
+ MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter, intParam);
+
+ if (mmsError)
+ *mmsError = err;
+
+exit_function:
+ return;
+}
+
struct writeMultipleVariablesParameter
{
Semaphore sem;
diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c
index e99a6e1b..fc2b3d62 100644
--- a/src/mms/iso_mms/client/mms_client_write.c
+++ b/src/mms/iso_mms/client/mms_client_write.c
@@ -529,6 +529,63 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
return rval.encoded;
}
+int
+mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component,
+ MmsValue* value,
+ ByteBuffer* writeBuffer)
+{
+ MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId);
+
+ mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present =
+ ConfirmedServiceRequest_PR_write;
+ WriteRequest_t* request =
+ &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write);
+
+ /* Create list of variable specifications */
+ request->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable;
+ request->variableAccessSpecification.choice.listOfVariable.list.count = 1;
+ request->variableAccessSpecification.choice.listOfVariable.list.array =
+ (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*));
+
+ ListOfVariableSeq_t* variableIdentifier = createNewDomainVariableSpecification(domainId, itemId);
+
+ request->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier;
+
+ variableIdentifier->alternateAccess = mmsClient_createAlternateAccessComponent(component);
+
+ /* Create list of typed data values */
+ request->listOfData.list.count = 1;
+ request->listOfData.list.size = 1;
+ request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(1, sizeof(struct Data*));
+ request->listOfData.list.array[0] = mmsMsg_createBasicDataElement(value);
+
+ /* Encode complete ASN1 structure */
+
+ asn_enc_rval_t rval;
+
+ rval = der_encode(&asn_DEF_MmsPdu, mmsPdu,
+ (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer);
+
+ /* Free ASN structure */
+ mmsClient_deleteAlternateAccess(variableIdentifier->alternateAccess);
+ request->variableAccessSpecification.choice.listOfVariable.list.count = 0;
+
+ GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array[0]);
+ GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array);
+ request->variableAccessSpecification.choice.listOfVariable.list.array = 0;
+
+ request->listOfData.list.count = 0;
+
+ deleteDataElement(request->listOfData.list.array[0]);
+
+ GLOBAL_FREEMEM(request->listOfData.list.array);
+ request->listOfData.list.array = 0;
+
+ asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+
+ return rval.encoded;
+}
+
int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component,
diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c
index 5d80a630..549619a8 100644
--- a/src/mms/iso_mms/common/mms_value.c
+++ b/src/mms/iso_mms/common/mms_value.c
@@ -763,8 +763,8 @@ MmsValue_setUtcTime(MmsValue* self, uint32_t timeval)
return self;
}
-MmsValue*
-MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval)
+static void
+setUtcTimeMs(MmsValue* self, uint64_t timeval, uint8_t timeQuality)
{
uint32_t timeval32 = (timeval / 1000LL);
@@ -786,7 +786,21 @@ MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval)
valueArray[6] = (fractionOfSecond & 0xff);
/* encode time quality */
- valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */
+ valueArray[7] = timeQuality;
+}
+
+MmsValue*
+MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval)
+{
+ setUtcTimeMs(self, timeval, 0x0a); /* set quality as 10 bit sub-second time accuracy */
+
+ return self;
+}
+
+MmsValue*
+MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality)
+{
+ setUtcTimeMs(self, timeval, timeQuality);
return self;
}
@@ -2191,7 +2205,7 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
const char* currentStr = MmsValue_printToBuffer((const MmsValue*) MmsValue_getElement(self, i), buffer + bufPos, bufferSize - bufPos);
- bufPos += strlen(currentStr);
+ bufPos += strnlen(currentStr, bufferSize - bufPos);
if (bufPos >= bufferSize)
break;
@@ -2226,9 +2240,13 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
int size = MmsValue_getBitStringSize(self);
/* fill buffer with zeros */
- if (size > bufferSize) {
+ if (size + 1 > bufferSize) {
memset(buffer, 0, bufferSize);
- break;
+
+ size = bufferSize - 1;
+
+ if (size < 1)
+ break;
}
int i;
diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c
index 005aa4c6..2170f8c1 100644
--- a/src/mms/iso_mms/server/mms_get_namelist_service.c
+++ b/src/mms/iso_mms/server/mms_get_namelist_service.c
@@ -1,7 +1,7 @@
/*
* mms_get_namelist_service.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -190,20 +190,29 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName)
MmsDomain* domain = MmsDevice_getDomain(device, domainName);
- if (domain != NULL) {
- nameList = LinkedList_create();
+ if (domain) {
- if (domain->journals != NULL) {
+ bool allowAccess = true;
- LinkedList journalList = domain->journals;
+ if (connection->server->getNameListHandler) {
+ allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_JOURNALS, domain, connection);
+ }
- while ((journalList = LinkedList_getNext(journalList)) != NULL) {
+ if (allowAccess) {
+ nameList = LinkedList_create();
- MmsJournal journal = (MmsJournal) LinkedList_getData(journalList);
+ if (domain->journals != NULL) {
- LinkedList_add(nameList, (void*) journal->name);
- }
+ LinkedList journalList = domain->journals;
+
+ while ((journalList = LinkedList_getNext(journalList)) != NULL) {
+
+ MmsJournal journal = (MmsJournal) LinkedList_getData(journalList);
+ LinkedList_add(nameList, (void*) journal->name);
+ }
+
+ }
}
}
@@ -219,46 +228,56 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName)
MmsDomain* domain = MmsDevice_getDomain(device, domainName);
- if (domain != NULL) {
- nameList = LinkedList_create();
- MmsVariableSpecification** variables = domain->namedVariables;
+ if (domain) {
- int i;
+ bool allowAccess = true;
- LinkedList element = nameList;
+ if (connection->server->getNameListHandler) {
+ allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection);
+ }
+
+ if (allowAccess) {
+ nameList = LinkedList_create();
+ MmsVariableSpecification** variables = domain->namedVariables;
+
+ int i;
+
+ LinkedList element = nameList;
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
- int* index = (int*) GLOBAL_MALLOC(sizeof(int) * domain->namedVariablesCount);
+ int* index = (int*) GLOBAL_MALLOC(sizeof(int) * domain->namedVariablesCount);
- for (i = 0; i < domain->namedVariablesCount; i++)
- index[i] = i;
+ for (i = 0; i < domain->namedVariablesCount; i++)
+ index[i] = i;
- sortIndex(index, domain->namedVariablesCount, domain->namedVariables);
+ sortIndex(index, domain->namedVariablesCount, domain->namedVariables);
#endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */
- for (i = 0; i < domain->namedVariablesCount; i++) {
+ for (i = 0; i < domain->namedVariablesCount; i++) {
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
- element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name));
+ element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name));
#else
- element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name));
+ element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name));
#endif
#if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1)
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
- char* prefix = variables[index[i]]->name;
- element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]);
+ char* prefix = variables[index[i]]->name;
+ element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]);
#else
- char* prefix = variables[i]->name;
- element = addSubNamedVaribleNamesToList(element, prefix, variables[i]);
+ char* prefix = variables[i]->name;
+ element = addSubNamedVaribleNamesToList(element, prefix, variables[i]);
#endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */
#endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */
- }
+ }
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
- GLOBAL_FREEMEM(index);
+ GLOBAL_FREEMEM(index);
#endif
+
+ }
}
return nameList;
@@ -293,10 +312,19 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain
MmsDomain* domain = MmsDevice_getDomain(device, domainName);
- if (domain != NULL) {
- LinkedList variableLists = MmsDomain_getNamedVariableLists(domain);
+ if (domain) {
+
+ bool allowAccess = true;
- nameList = createStringsFromNamedVariableList(variableLists);
+ if (connection->server->getNameListHandler) {
+ allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection);
+ }
+
+ if (allowAccess) {
+ LinkedList variableLists = MmsDomain_getNamedVariableLists(domain);
+
+ nameList = createStringsFromNamedVariableList(variableLists);
+ }
}
return nameList;
@@ -613,38 +641,79 @@ mmsServer_handleGetNameListRequest(
if (objectClass == OBJECT_CLASS_DOMAIN) {
- LinkedList nameList = getDomainNames(connection);
+ bool allowAccess = true;
+
+ if (connection->server->getNameListHandler) {
+ allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DOMAINS, NULL, connection);
+ }
+
+ if (allowAccess) {
+ LinkedList nameList = getDomainNames(connection);
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
- StringUtils_sortList(nameList);
+ StringUtils_sortList(nameList);
#endif
- createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
+ createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
- LinkedList_destroyStatic(nameList);
+ LinkedList_destroyStatic(nameList);
+ }
+ else {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
+ }
}
#if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) {
- LinkedList nameList = getNameListVMDSpecific(connection);
- createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
+ bool allowAccess = true;
+
+ if (connection->server->getNameListHandler) {
+ allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, NULL, connection);
+ }
+
+ if (allowAccess) {
+ LinkedList nameList = getNameListVMDSpecific(connection);
+
+#if (CONFIG_MMS_SORT_NAME_LIST == 1)
+ StringUtils_sortList(nameList);
+#endif
+
+ createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
- LinkedList_destroyStatic(nameList);
+ LinkedList_destroyStatic(nameList);
+ }
+ else {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
+ }
}
#endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */
#if (MMS_DATA_SET_SERVICE == 1)
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) {
- LinkedList nameList = getNamedVariableListsVMDSpecific(connection);
+
+ bool allowAccess = true;
+
+ if (connection->server->getNameListHandler) {
+ allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, NULL, connection);
+ }
+
+ if (allowAccess) {
+
+ LinkedList nameList = getNamedVariableListsVMDSpecific(connection);
#if (CONFIG_MMS_SORT_NAME_LIST == 1)
- StringUtils_sortList(nameList);
+ StringUtils_sortList(nameList);
#endif
- createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
+ createNameListResponse(connection, invokeId, nameList, response, continueAfterId);
- LinkedList_destroy(nameList);
+ LinkedList_destroy(nameList);
+
+ }
+ else {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
+ }
}
#endif /* (MMS_DATA_SET_SERVICE == 1) */
diff --git a/src/mms/iso_mms/server/mms_journal_service.c b/src/mms/iso_mms/server/mms_journal_service.c
index 22980f76..0aadde40 100644
--- a/src/mms/iso_mms/server/mms_journal_service.c
+++ b/src/mms/iso_mms/server/mms_journal_service.c
@@ -462,6 +462,20 @@ mmsServer_handleReadJournalRequest(
if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name);
+ MmsServer mmsServer = connection->server;
+
+ if (mmsServer->readJournalHandler)
+ {
+ if (mmsServer->readJournalHandler(mmsServer->readJournalHandlerParameter, mmsDomain, logName, connection) == false)
+ {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
+
+ /* TODO log access error */
+
+ return;
+ }
+ }
+
struct sJournalEncoder encoder;
encoder.buffer = response->buffer;
diff --git a/src/mms/iso_mms/server/mms_named_variable_list.c b/src/mms/iso_mms/server/mms_named_variable_list.c
index 31694780..3c5ea5c3 100644
--- a/src/mms/iso_mms/server/mms_named_variable_list.c
+++ b/src/mms/iso_mms/server/mms_named_variable_list.c
@@ -3,22 +3,22 @@
*
* Copyright 2013 Michael Zillgith
*
- * This file is part of libIEC61850.
+ * This file is part of libIEC61850.
*
- * libIEC61850 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * libIEC61850 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
*
- * libIEC61850 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.
+ * libIEC61850 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 libIEC61850. If not, see .
+ * You should have received a copy of the GNU General Public License
+ * along with libIEC61850. If not, see .
*
- * See COPYING file for the complete license text.
+ * See COPYING file for the complete license text.
*/
#include "libiec61850_platform_includes.h"
@@ -51,7 +51,6 @@ MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self)
GLOBAL_FREEMEM(self);
}
-
MmsDomain*
MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self)
{
@@ -120,5 +119,3 @@ MmsNamedVariableList_destroy(MmsNamedVariableList self)
GLOBAL_FREEMEM(self->name);
GLOBAL_FREEMEM(self);
}
-
-
diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c
index 3a27061c..85f1b960 100644
--- a/src/mms/iso_mms/server/mms_named_variable_list_service.c
+++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c
@@ -1,7 +1,7 @@
/*
* mms_named_variable_list_service.c
*
- * Copyright 2013-2020 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -49,67 +49,67 @@
#endif
MmsError
-mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain,
- char* listName, MmsServerConnection connection)
+mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
+ char* listName, MmsServerConnection connection)
{
- MmsServer self = connection->server;
+ MmsServer self = connection->server;
- if (self->variableListChangedHandler != NULL) {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: call MmsNamedVariableListChangedHandler for new list %s\n", listName);
+ if (self->variableListAccessHandler != NULL) {
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: call MmsNamedVariableListAccessHandler for new list %s\n", listName);
- return self->variableListChangedHandler(self->variableListChangedHandlerParameter,
- create, listType, domain, listName, connection);
- }
- else
- return MMS_ERROR_NONE;
+ return self->variableListAccessHandler(self->variableListAccessHandlerParameter,
+ accessType, listType, domain, listName, connection);
+ }
+ else
+ return MMS_ERROR_NONE;
}
static void
createDeleteNamedVariableListResponse(uint32_t invokeId, ByteBuffer* response,
- uint32_t numberMatched, uint32_t numberDeleted)
+ uint32_t numberMatched, uint32_t numberDeleted)
{
- uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2;
+ uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2;
- uint32_t numberMatchedSize =
- 2 + BerEncoder_UInt32determineEncodedSize(numberMatched);
+ uint32_t numberMatchedSize =
+ 2 + BerEncoder_UInt32determineEncodedSize(numberMatched);
- uint32_t numberDeletedSize =
- 2 + BerEncoder_UInt32determineEncodedSize(numberDeleted);
+ uint32_t numberDeletedSize =
+ 2 + BerEncoder_UInt32determineEncodedSize(numberDeleted);
- uint32_t deleteNVLSize = 2 + numberMatchedSize + numberDeletedSize;
+ uint32_t deleteNVLSize = 2 + numberMatchedSize + numberDeletedSize;
- uint32_t confirmedResponsePDUSize = invokeIdSize + deleteNVLSize;
+ uint32_t confirmedResponsePDUSize = invokeIdSize + deleteNVLSize;
- int bufPos = 0;
- uint8_t* buffer = response->buffer;
+ int bufPos = 0;
+ uint8_t* buffer = response->buffer;
- bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
- bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
- bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
+ bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
- bufPos = BerEncoder_encodeTL(0xad, numberMatchedSize + numberDeletedSize, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0xad, numberMatchedSize + numberDeletedSize, buffer, bufPos);
- bufPos = BerEncoder_encodeTL(0x80, numberMatchedSize - 2, buffer, bufPos);
- bufPos = BerEncoder_encodeUInt32(numberMatched, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0x80, numberMatchedSize - 2, buffer, bufPos);
+ bufPos = BerEncoder_encodeUInt32(numberMatched, buffer, bufPos);
- bufPos = BerEncoder_encodeTL(0x81, numberDeletedSize - 2, buffer, bufPos);
- bufPos = BerEncoder_encodeUInt32(numberDeleted, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0x81, numberDeletedSize - 2, buffer, bufPos);
+ bufPos = BerEncoder_encodeUInt32(numberDeleted, buffer, bufPos);
- response->size = bufPos;
+ response->size = bufPos;
}
static void /* Confirmed service error (ServiceError) */
createServiceErrorDeleteVariableLists(uint32_t invokeId, ByteBuffer* response,
- MmsError errorType, uint32_t numberDeleted)
+ MmsError errorType, uint32_t numberDeleted)
{
- uint8_t buffer[8];
+ uint8_t buffer[8];
- int size = BerEncoder_encodeUInt32WithTL(0x86, numberDeleted, buffer, 0);
+ int size = BerEncoder_encodeUInt32WithTL(0x86, numberDeleted, buffer, 0);
- mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType,
- buffer, size);
+ mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType,
+ buffer, size);
}
void
@@ -118,38 +118,38 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection,
uint32_t invokeId,
ByteBuffer* response)
{
- (void)bufPos;
-
- DeleteNamedVariableListRequest_t* request = NULL;
- MmsPdu_t* mmsPdu = NULL;
-
- asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos);
-
- if (rval.code != RC_OK) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
- goto exit_function;
- }
-
- if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) &&
- (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present
- == ConfirmedServiceRequest_PR_deleteNamedVariableList))
- {
- request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList);
- }
- else {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
- goto exit_function;
- }
+ (void)bufPos;
+
+ DeleteNamedVariableListRequest_t* request = NULL;
+ MmsPdu_t* mmsPdu = NULL;
+
+ asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos);
+
+ if (rval.code != RC_OK) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ goto exit_function;
+ }
+
+ if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) &&
+ (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present
+ == ConfirmedServiceRequest_PR_deleteNamedVariableList))
+ {
+ request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList);
+ }
+ else {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ goto exit_function;
+ }
long scopeOfDelete = DeleteNamedVariableListRequest__scopeOfDelete_specific;
if (request->scopeOfDelete)
- asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete);
+ asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete);
MmsDevice* device = MmsServer_getDevice(connection->server);
if (scopeOfDelete == DeleteNamedVariableListRequest__scopeOfDelete_specific) {
- MmsError serviceError = MMS_ERROR_NONE;
+ MmsError serviceError = MMS_ERROR_NONE;
int numberMatched = 0;
int numberDeleted = 0;
@@ -160,86 +160,86 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection,
for (i = 0; i < numberItems; i++) {
if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_domainspecific) {
- char domainName[65];
- char listName[65];
+ char domainName[65];
+ char listName[65];
- mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.domainId,
- domainName, 65);
+ mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.domainId,
+ domainName, 65);
- mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.itemId,
- listName, 65);
+ mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.itemId,
+ listName, 65);
- MmsDomain* domain = MmsDevice_getDomain(device, domainName);
+ MmsDomain* domain = MmsDevice_getDomain(device, domainName);
- if (domain != NULL) {
+ if (domain != NULL) {
- MmsNamedVariableList variableList = MmsDomain_getNamedVariableList(domain, listName);
+ MmsNamedVariableList variableList = MmsDomain_getNamedVariableList(domain, listName);
- if (variableList != NULL) {
- numberMatched++;
+ if (variableList != NULL) {
+ numberMatched++;
- if (MmsNamedVariableList_isDeletable(variableList)) {
+ if (MmsNamedVariableList_isDeletable(variableList)) {
- MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_DOMAIN_SPECIFIC, domain, listName, connection);
+ MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_DOMAIN_SPECIFIC, domain, listName, connection);
- if (deleteError == MMS_ERROR_NONE) {
- MmsDomain_deleteNamedVariableList(domain, listName);
- numberDeleted++;
- }
- else
- serviceError = deleteError;
- }
- }
- }
+ if (deleteError == MMS_ERROR_NONE) {
+ MmsDomain_deleteNamedVariableList(domain, listName);
+ numberDeleted++;
+ }
+ else
+ serviceError = deleteError;
+ }
+ }
+ }
}
else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_aaspecific) {
- char listName[65];
+ char listName[65];
- mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.aaspecific,
- listName, 65);
+ mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.aaspecific,
+ listName, 65);
MmsNamedVariableList variableList = MmsServerConnection_getNamedVariableList(connection, listName);
if (variableList != NULL) {
numberMatched++;
- MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection);
+ MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection);
if (deleteError == MMS_ERROR_NONE) {
- numberDeleted++;
- MmsServerConnection_deleteNamedVariableList(connection, listName);
+ numberDeleted++;
+ MmsServerConnection_deleteNamedVariableList(connection, listName);
}
else
- serviceError = deleteError;
+ serviceError = deleteError;
}
}
else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_vmdspecific) {
- char listName[65];
+ char listName[65];
- mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.vmdspecific,
- listName, 65);
+ mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.vmdspecific,
+ listName, 65);
- MmsNamedVariableList variableList = mmsServer_getNamedVariableListWithName(device->namedVariableLists, listName);
+ MmsNamedVariableList variableList = mmsServer_getNamedVariableListWithName(device->namedVariableLists, listName);
- if (variableList != NULL) {
- numberMatched++;
+ if (variableList != NULL) {
+ numberMatched++;
- MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_VMD_SPECIFIC, NULL, listName, connection);
+ MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_VMD_SPECIFIC, NULL, listName, connection);
- if (deleteError == MMS_ERROR_NONE) {
- numberDeleted++;
- mmsServer_deleteVariableList(device->namedVariableLists, listName);
- }
- else
- serviceError = deleteError;
- }
+ if (deleteError == MMS_ERROR_NONE) {
+ numberDeleted++;
+ mmsServer_deleteVariableList(device->namedVariableLists, listName);
+ }
+ else
+ serviceError = deleteError;
+ }
}
}
if (serviceError == MMS_ERROR_NONE)
- createDeleteNamedVariableListResponse(invokeId, response, numberMatched, numberDeleted);
+ createDeleteNamedVariableListResponse(invokeId, response, numberMatched, numberDeleted);
else
- createServiceErrorDeleteVariableLists(invokeId, response, serviceError, numberDeleted);
+ createServiceErrorDeleteVariableLists(invokeId, response, serviceError, numberDeleted);
}
else {
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
@@ -247,61 +247,61 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection,
exit_function:
- asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
+ asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
- return;
+ return;
}
static void
createDefineNamedVariableListResponse(uint32_t invokeId, ByteBuffer* response)
{
- uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2;
+ uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2;
- uint32_t confirmedResponsePDUSize = 2 + invokeIdSize;
+ uint32_t confirmedResponsePDUSize = 2 + invokeIdSize;
- int bufPos = 0;
- uint8_t* buffer = response->buffer;
+ int bufPos = 0;
+ uint8_t* buffer = response->buffer;
- bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos);
- bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
- bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos);
+ bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos);
- bufPos = BerEncoder_encodeTL(0x8b, 0, buffer, bufPos);
+ bufPos = BerEncoder_encodeTL(0x8b, 0, buffer, bufPos);
- response->size = bufPos;
+ response->size = bufPos;
}
static bool
checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier)
{
- (void)device;
+ (void)device;
- if (accessSpecifier->domain == NULL)
- return false;
+ if (accessSpecifier->domain == NULL)
+ return false;
- MmsVariableSpecification* variableSpec =
- MmsDomain_getNamedVariable(accessSpecifier->domain, accessSpecifier->variableName);
+ MmsVariableSpecification* variableSpec =
+ MmsDomain_getNamedVariable(accessSpecifier->domain, accessSpecifier->variableName);
- if (variableSpec == NULL)
- return false;
+ if (variableSpec == NULL)
+ return false;
- if (accessSpecifier->arrayIndex != -1) {
- if (variableSpec->type != MMS_ARRAY)
- return false;
+ if (accessSpecifier->arrayIndex != -1) {
+ if (variableSpec->type != MMS_ARRAY)
+ return false;
- if (accessSpecifier->arrayIndex >= variableSpec->typeSpec.array.elementCount)
- return false;
+ if (accessSpecifier->arrayIndex >= variableSpec->typeSpec.array.elementCount)
+ return false;
- if (accessSpecifier->componentName != NULL) {
- variableSpec = variableSpec->typeSpec.array.elementTypeSpec;
+ if (accessSpecifier->componentName != NULL) {
+ variableSpec = variableSpec->typeSpec.array.elementTypeSpec;
- if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL)
- return false;
- }
- }
+ if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL)
+ return false;
+ }
+ }
- return true;
+ return true;
}
static MmsNamedVariableList
@@ -309,20 +309,20 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
DefineNamedVariableListRequest_t* request,
char* variableListName, MmsError* mmsError)
{
- MmsNamedVariableList namedVariableList = NULL;
+ MmsNamedVariableList namedVariableList = NULL;
int variableCount = request->listOfVariable.list.count;
if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries);
+ printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries);
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
if ((variableCount == 0 ) || (variableCount > server->maxDataSetEntries)) {
#else
if ((variableCount == 0 ) || (variableCount > CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS)) {
#endif
- *mmsError = MMS_ERROR_DEFINITION_OTHER;
- goto exit_function;
+ *mmsError = MMS_ERROR_DEFINITION_OTHER;
+ goto exit_function;
}
namedVariableList = MmsNamedVariableList_create(domain, variableListName, true);
@@ -342,8 +342,8 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
if (request->listOfVariable.list.array[i]->alternateAccess->list.count != 1) {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n");
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n");
MmsNamedVariableList_destroy(namedVariableList);
namedVariableList = NULL;
@@ -355,31 +355,31 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
request->listOfVariable.list.array[i]->alternateAccess->list.array[0];
if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed)
- && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess)
- && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present ==
- AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index))
+ && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess)
+ && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present ==
+ AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index))
{
asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index),
&arrayIndex);
if (alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess) {
- componentNameBuf[0] = 0;
+ componentNameBuf[0] = 0;
- componentName = mmsMsg_getComponentNameFromAlternateAccess(
- alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
- componentNameBuf, 0);
+ componentName = mmsMsg_getComponentNameFromAlternateAccess(
+ alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
+ componentNameBuf, 0);
+ }
+ else {
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n");
}
- else {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n");
- }
}
else if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed)
- && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess)
- && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index)
- )
+ && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess)
+ && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index)
+ )
{
- asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex);
+ asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex);
}
else {
MmsNamedVariableList_destroy(namedVariableList);
@@ -392,16 +392,16 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
if (varSpec->present == VariableSpecification_PR_name) {
- char variableName[65];
- char domainId[65];
+ char variableName[65];
+ char domainId[65];
- StringUtils_createStringFromBufferInBuffer(variableName,
- varSpec->choice.name.choice.domainspecific.itemId.buf,
- varSpec->choice.name.choice.domainspecific.itemId.size);
+ StringUtils_createStringFromBufferInBuffer(variableName,
+ varSpec->choice.name.choice.domainspecific.itemId.buf,
+ varSpec->choice.name.choice.domainspecific.itemId.size);
- StringUtils_createStringFromBufferInBuffer(domainId,
- varSpec->choice.name.choice.domainspecific.domainId.buf,
- varSpec->choice.name.choice.domainspecific.domainId.size);
+ StringUtils_createStringFromBufferInBuffer(domainId,
+ varSpec->choice.name.choice.domainspecific.domainId.buf,
+ varSpec->choice.name.choice.domainspecific.domainId.size);
MmsDomain* elementDomain = MmsDevice_getDomain(device, domainId);
@@ -413,24 +413,24 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device,
accessSpecifier.componentName = componentName;
if (DEBUG_MMS_SERVER)
- printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName);
+ printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName);
/* check if element exists */
if (checkIfVariableExists(device, &accessSpecifier) == true) {
- MmsNamedVariableListEntry variable =
- MmsNamedVariableListEntry_create(accessSpecifier);
+ MmsNamedVariableListEntry variable =
+ MmsNamedVariableListEntry_create(accessSpecifier);
- MmsNamedVariableList_addVariable(namedVariableList, variable);
+ MmsNamedVariableList_addVariable(namedVariableList, variable);
}
else {
- if (DEBUG_MMS_SERVER)
- printf("MMS_SERVER: failed - variable does not exist!\n");
+ if (DEBUG_MMS_SERVER)
+ printf("MMS_SERVER: failed - variable does not exist!\n");
- MmsNamedVariableList_destroy(namedVariableList);
- namedVariableList = NULL;
- i = variableCount; /* exit loop after freeing loop variables */
- *mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED;
+ MmsNamedVariableList_destroy(namedVariableList);
+ namedVariableList = NULL;
+ i = variableCount; /* exit loop after freeing loop variables */
+ *mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED;
}
}
else {
@@ -453,7 +453,7 @@ mmsServer_handleDefineNamedVariableListRequest(
uint32_t invokeId,
ByteBuffer* response)
{
- (void)bufPos;
+ (void)bufPos;
DefineNamedVariableListRequest_t* request = 0;
@@ -462,177 +462,177 @@ mmsServer_handleDefineNamedVariableListRequest(
asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos);
if (rval.code != RC_OK) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
- goto exit_free_struct;
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ goto exit_free_struct;
}
- if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) &&
- (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present
- == ConfirmedServiceRequest_PR_defineNamedVariableList))
- {
- request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList);
- }
- else {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
- goto exit_free_struct;
- }
+ if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) &&
+ (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present
+ == ConfirmedServiceRequest_PR_defineNamedVariableList))
+ {
+ request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList);
+ }
+ else {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ goto exit_free_struct;
+ }
- MmsDevice* device = MmsServer_getDevice(connection->server);
+ MmsDevice* device = MmsServer_getDevice(connection->server);
- if (request->variableListName.present == ObjectName_PR_domainspecific) {
+ if (request->variableListName.present == ObjectName_PR_domainspecific) {
- char domainName[65];
+ char domainName[65];
- if (request->variableListName.choice.domainspecific.domainId.size > 64) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
- goto exit_free_struct;
- }
+ if (request->variableListName.choice.domainspecific.domainId.size > 64) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ goto exit_free_struct;
+ }
- StringUtils_createStringFromBufferInBuffer(domainName,
- request->variableListName.choice.domainspecific.domainId.buf,
- request->variableListName.choice.domainspecific.domainId.size);
+ StringUtils_createStringFromBufferInBuffer(domainName,
+ request->variableListName.choice.domainspecific.domainId.buf,
+ request->variableListName.choice.domainspecific.domainId.size);
- MmsDomain* domain = MmsDevice_getDomain(device, domainName);
+ MmsDomain* domain = MmsDevice_getDomain(device, domainName);
- if (domain == NULL) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
- goto exit_free_struct;
- }
+ if (domain == NULL) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ goto exit_free_struct;
+ }
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
- if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) {
+ if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) {
#else
- if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) {
+ if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) {
#endif
- char variableListName[65];
-
- if (request->variableListName.choice.domainspecific.itemId.size > 64) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
- goto exit_free_struct;
- }
-
- StringUtils_createStringFromBufferInBuffer(variableListName,
- request->variableListName.choice.domainspecific.itemId.buf,
- request->variableListName.choice.domainspecific.itemId.size);
-
- if (MmsDomain_getNamedVariableList(domain, variableListName) != NULL) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS);
- }
- else {
- MmsError mmsError;
-
- MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device,
- request, variableListName, &mmsError);
-
- if (namedVariableList != NULL) {
-
- mmsError = mmsServer_callVariableListChangedHandler(true, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection);
-
- if (mmsError == MMS_ERROR_NONE) {
- MmsDomain_addNamedVariableList(domain, namedVariableList);
- createDefineNamedVariableListResponse(invokeId, response);
- }
- else {
- MmsNamedVariableList_destroy(namedVariableList);
- mmsMsg_createServiceErrorPdu(invokeId, response, mmsError);
- }
- }
- else
- mmsMsg_createServiceErrorPdu(invokeId, response, mmsError);
- }
+ char variableListName[65];
+
+ if (request->variableListName.choice.domainspecific.itemId.size > 64) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ goto exit_free_struct;
+ }
+
+ StringUtils_createStringFromBufferInBuffer(variableListName,
+ request->variableListName.choice.domainspecific.itemId.buf,
+ request->variableListName.choice.domainspecific.itemId.size);
+
+ if (MmsDomain_getNamedVariableList(domain, variableListName) != NULL) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS);
+ }
+ else {
+ MmsError mmsError;
+
+ MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device,
+ request, variableListName, &mmsError);
+
+ if (namedVariableList != NULL) {
+
+ mmsError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection);
+
+ if (mmsError == MMS_ERROR_NONE) {
+ MmsDomain_addNamedVariableList(domain, namedVariableList);
+ createDefineNamedVariableListResponse(invokeId, response);
+ }
+ else {
+ MmsNamedVariableList_destroy(namedVariableList);
+ mmsMsg_createServiceErrorPdu(invokeId, response, mmsError);
+ }
+ }
+ else
+ mmsMsg_createServiceErrorPdu(invokeId, response, mmsError);
+ }
}
else
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE);
}
else if (request->variableListName.present == ObjectName_PR_aaspecific) {
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
- if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) {
+ if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) {
#else
- if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) {
+ if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) {
#endif
- char variableListName[65];
-
- if (request->variableListName.choice.aaspecific.size > 64) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
- goto exit_free_struct;
- }
-
- StringUtils_createStringFromBufferInBuffer(variableListName,
- request->variableListName.choice.aaspecific.buf,
- request->variableListName.choice.aaspecific.size);
-
- if (MmsServerConnection_getNamedVariableList(connection, variableListName) != NULL) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS);
- }
- else {
- MmsError mmsError;
-
- MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device,
- request, variableListName, &mmsError);
-
- if (namedVariableList != NULL) {
-
- if (mmsServer_callVariableListChangedHandler(true, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) {
- MmsServerConnection_addNamedVariableList(connection, namedVariableList);
- createDefineNamedVariableListResponse(invokeId, response);
- }
- else {
- MmsNamedVariableList_destroy(namedVariableList);
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
- }
-
- }
- else
- mmsMsg_createServiceErrorPdu(invokeId, response, mmsError);
- }
- }
- else
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE);
+ char variableListName[65];
+
+ if (request->variableListName.choice.aaspecific.size > 64) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
+ goto exit_free_struct;
+ }
+
+ StringUtils_createStringFromBufferInBuffer(variableListName,
+ request->variableListName.choice.aaspecific.buf,
+ request->variableListName.choice.aaspecific.size);
+
+ if (MmsServerConnection_getNamedVariableList(connection, variableListName) != NULL) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS);
+ }
+ else {
+ MmsError mmsError;
+
+ MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device,
+ request, variableListName, &mmsError);
+
+ if (namedVariableList != NULL) {
+
+ if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) {
+ MmsServerConnection_addNamedVariableList(connection, namedVariableList);
+ createDefineNamedVariableListResponse(invokeId, response);
+ }
+ else {
+ MmsNamedVariableList_destroy(namedVariableList);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
+ }
+
+ }
+ else
+ mmsMsg_createServiceErrorPdu(invokeId, response, mmsError);
+ }
+ }
+ else
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE);
}
else if (request->variableListName.present == ObjectName_PR_vmdspecific) {
- LinkedList vmdScopeNVLs = MmsDevice_getNamedVariableLists(connection->server->device);
+ LinkedList vmdScopeNVLs = MmsDevice_getNamedVariableLists(connection->server->device);
- if (LinkedList_size(vmdScopeNVLs) < CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS) {
+ if (LinkedList_size(vmdScopeNVLs) < CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS) {
- char variableListName[65];
+ char variableListName[65];
- if (request->variableListName.choice.vmdspecific.size > 64) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
- goto exit_free_struct;
- }
+ if (request->variableListName.choice.vmdspecific.size > 64) {
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response);
+ goto exit_free_struct;
+ }
- StringUtils_createStringFromBufferInBuffer(variableListName,
- request->variableListName.choice.vmdspecific.buf,
- request->variableListName.choice.vmdspecific.size);
+ StringUtils_createStringFromBufferInBuffer(variableListName,
+ request->variableListName.choice.vmdspecific.buf,
+ request->variableListName.choice.vmdspecific.size);
- if (mmsServer_getNamedVariableListWithName(MmsDevice_getNamedVariableLists(connection->server->device), variableListName) != NULL) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS);
- }
- else {
- MmsError mmsError;
+ if (mmsServer_getNamedVariableListWithName(MmsDevice_getNamedVariableLists(connection->server->device), variableListName) != NULL) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS);
+ }
+ else {
+ MmsError mmsError;
- MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device,
- request, variableListName, &mmsError);
+ MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device,
+ request, variableListName, &mmsError);
- if (namedVariableList != NULL) {
- if (mmsServer_callVariableListChangedHandler(true, MMS_VMD_SPECIFIC, NULL, variableListName, connection)
- == MMS_ERROR_NONE) {
- LinkedList_add(vmdScopeNVLs, (void*) namedVariableList);
+ if (namedVariableList != NULL) {
+ if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_VMD_SPECIFIC, NULL, variableListName, connection)
+ == MMS_ERROR_NONE) {
+ LinkedList_add(vmdScopeNVLs, (void*) namedVariableList);
- createDefineNamedVariableListResponse(invokeId, response);
- }
- else {
- MmsNamedVariableList_destroy(namedVariableList);
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
- }
+ createDefineNamedVariableListResponse(invokeId, response);
+ }
+ else {
+ MmsNamedVariableList_destroy(namedVariableList);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED);
+ }
- }
- }
- }
+ }
+ }
+ }
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_TYPE_UNSUPPORTED);
@@ -640,7 +640,7 @@ mmsServer_handleDefineNamedVariableListRequest(
exit_free_struct:
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
- return;
+ return;
}
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
@@ -670,7 +670,7 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
varListResponse->listOfVariable.list.size = variableCount;
varListResponse->listOfVariable.list.array = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member**)
- GLOBAL_CALLOC(variableCount, sizeof(void*));
+ GLOBAL_CALLOC(variableCount, sizeof(void*));
LinkedList variable = LinkedList_getNext(variables);
@@ -679,7 +679,7 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
MmsNamedVariableListEntry variableEntry = (MmsNamedVariableListEntry) variable->data;
varListResponse->listOfVariable.list.array[i] = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member*)
- GLOBAL_CALLOC(1, sizeof(struct GetNamedVariableListAttributesResponse__listOfVariable__Member));
+ GLOBAL_CALLOC(1, sizeof(struct GetNamedVariableListAttributesResponse__listOfVariable__Member));
varListResponse->listOfVariable.list.array[i]->variableSpecification.present =
VariableSpecification_PR_name;
@@ -702,12 +702,12 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
domainspecific.itemId.size = strlen(variableEntry->variableName);
if (variableEntry->arrayIndex != -1) {
- varListResponse->listOfVariable.list.array[i]->alternateAccess =
- mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName);
+ varListResponse->listOfVariable.list.array[i]->alternateAccess =
+ mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName);
}
else if (variableEntry->componentName) {
- varListResponse->listOfVariable.list.array[i]->alternateAccess =
- mmsClient_createAlternateAccessComponent(variableEntry->componentName);
+ varListResponse->listOfVariable.list.array[i]->alternateAccess =
+ mmsClient_createAlternateAccessComponent(variableEntry->componentName);
}
variable = LinkedList_getNext(variable);
@@ -718,9 +718,9 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response,
asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0);
if (res.encoded == -1)
- return false;
+ return false;
else
- return true;
+ return true;
}
void
@@ -736,44 +736,53 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
(void**) &request, buffer + bufPos, maxBufPos - bufPos);
if (rval.code != RC_OK) {
- mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
- goto exit_function;
+ mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response);
+ goto exit_function;
}
if (request->present == ObjectName_PR_domainspecific) {
- char domainName[65];
- char itemName[65];
+ char domainName[65];
+ char itemName[65];
- if ((request->choice.domainspecific.domainId.size > 64) ||
- (request->choice.domainspecific.itemId.size > 64)) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER);
- goto exit_function;
- }
+ if ((request->choice.domainspecific.domainId.size > 64) ||
+ (request->choice.domainspecific.itemId.size > 64)) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER);
+ goto exit_function;
+ }
- StringUtils_createStringFromBufferInBuffer(domainName, request->choice.domainspecific.domainId.buf,
- request->choice.domainspecific.domainId.size);
+ StringUtils_createStringFromBufferInBuffer(domainName, request->choice.domainspecific.domainId.buf,
+ request->choice.domainspecific.domainId.size);
- StringUtils_createStringFromBufferInBuffer(itemName, request->choice.domainspecific.itemId.buf,
- request->choice.domainspecific.itemId.size);
+ StringUtils_createStringFromBufferInBuffer(itemName, request->choice.domainspecific.itemId.buf,
+ request->choice.domainspecific.itemId.size);
MmsDevice* mmsDevice = MmsServer_getDevice(connection->server);
MmsDomain* domain = MmsDevice_getDomain(mmsDevice, domainName);
if (domain != NULL) {
- MmsNamedVariableList variableList =
- MmsDomain_getNamedVariableList(domain, itemName);
+ MmsNamedVariableList varList = MmsDomain_getNamedVariableList(domain, itemName);
- if (variableList != NULL) {
+ if (varList) {
- if (createGetNamedVariableListAttributesResponse(invokeId, response, variableList) == false) {
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_DOMAIN_SPECIFIC, domain, varList->name, connection);
- /* encoding failed - probably because buffer size is too small for message */
- ByteBuffer_setSize(response, 0);
+ if (accessError == MMS_ERROR_NONE) {
+ if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
+ /* encoding failed - probably because buffer size is too small for message */
+ ByteBuffer_setSize(response, 0);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
+ }
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
}
+
}
else
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
@@ -785,43 +794,77 @@ mmsServer_handleGetNamedVariableListAttributesRequest(
#if (MMS_DYNAMIC_DATA_SETS == 1)
else if (request->present == ObjectName_PR_aaspecific) {
- char listName[65];
+ char listName[65];
- if (request->choice.aaspecific.size > 64) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER);
- goto exit_function;
- }
+ if (request->choice.aaspecific.size > 64) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER);
+ goto exit_function;
+ }
+
+ StringUtils_createStringFromBufferInBuffer(listName, request->choice.aaspecific.buf,
+ request->choice.aaspecific.size);
+
+ MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName);
+
+ if (varList) {
+
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_ASSOCIATION_SPECIFIC, NULL, varList->name, connection);
- StringUtils_createStringFromBufferInBuffer(listName, request->choice.aaspecific.buf,
- request->choice.aaspecific.size);
+ if (accessError == MMS_ERROR_NONE) {
+ if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) {
- MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName);
+ /* encoding failed - probably because buffer size is too small for message */
+ ByteBuffer_setSize(response, 0);
- if (varList != NULL)
- createGetNamedVariableListAttributesResponse(invokeId, response, varList);
- else
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
+ }
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
+ }
+ else
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
}
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
else if (request->present == ObjectName_PR_vmdspecific) {
- char listName[65];
+ char listName[65];
+
+ if (request->choice.vmdspecific.size > 64) {
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER);
+ goto exit_function;
+ }
+
+ StringUtils_createStringFromBufferInBuffer(listName, request->choice.vmdspecific.buf,
+ request->choice.vmdspecific.size);
+
+ MmsDevice* mmsDevice = MmsServer_getDevice(connection->server);
- if (request->choice.vmdspecific.size > 64) {
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER);
- goto exit_function;
- }
+ MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName);
- StringUtils_createStringFromBufferInBuffer(listName, request->choice.vmdspecific.buf,
- request->choice.vmdspecific.size);
+ if (varList) {
- MmsDevice* mmsDevice = MmsServer_getDevice(connection->server);
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_VMD_SPECIFIC, NULL, varList->name, connection);
- MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName);
+ if (accessError == MMS_ERROR_NONE) {
+ if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) {
- if (varList != NULL)
- createGetNamedVariableListAttributesResponse(invokeId, response, varList);
- else
- mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
+ /* encoding failed - probably because buffer size is too small for message */
+ ByteBuffer_setSize(response, 0);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
+ }
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
+ }
+ else
+ mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
}
else {
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED);
diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c
index 2638bc0b..2b33dc41 100644
--- a/src/mms/iso_mms/server/mms_read_service.c
+++ b/src/mms/iso_mms/server/mms_read_service.c
@@ -1,7 +1,7 @@
/*
* mms_read_service.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -695,13 +695,9 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable
LinkedList /**/ values = LinkedList_create();
LinkedList variables = MmsNamedVariableList_getVariableList(namedList);
- int variableCount = LinkedList_size(variables);
-
- int i;
-
LinkedList variable = LinkedList_getNext(variables);
- for (i = 0; i < variableCount; i++) {
+ while (variable) {
MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data;
@@ -768,9 +764,21 @@ handleReadNamedVariableListRequest(
else {
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
- if (namedList != NULL) {
- createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read),
- &accessSpec);
+ if (namedList)
+ {
+
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_DOMAIN_SPECIFIC, domain, namedList->name, connection);
+
+ if (accessError == MMS_ERROR_NONE) {
+ createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read),
+ &accessSpec);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s access error: %i\n", nameIdStr, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
+
}
else {
if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s not found!\n", nameIdStr);
@@ -791,14 +799,24 @@ handleReadNamedVariableListRequest(
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else {
- VarAccessSpec accessSpec;
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_VMD_SPECIFIC, NULL, namedList->name, connection);
+
+ if (accessError == MMS_ERROR_NONE) {
+ VarAccessSpec accessSpec;
- accessSpec.isNamedVariableList = true;
- accessSpec.specific = 0;
- accessSpec.domainId = NULL;
- accessSpec.itemId = listName;
+ accessSpec.isNamedVariableList = true;
+ accessSpec.specific = 0;
+ accessSpec.domainId = NULL;
+ accessSpec.itemId = listName;
+
+ createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS read: VMD specific named variable list %s access error: %i\n", listName, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
- createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec);
}
}
#if (MMS_DYNAMIC_DATA_SETS == 1)
@@ -815,14 +833,25 @@ handleReadNamedVariableListRequest(
if (namedList == NULL)
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else {
- VarAccessSpec accessSpec;
- accessSpec.isNamedVariableList = true;
- accessSpec.specific = 2;
- accessSpec.domainId = NULL;
- accessSpec.itemId = listName;
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection);
+
+ if (accessError == MMS_ERROR_NONE) {
- createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec);
+ VarAccessSpec accessSpec;
+
+ accessSpec.isNamedVariableList = true;
+ accessSpec.specific = 2;
+ accessSpec.domainId = NULL;
+ accessSpec.itemId = listName;
+
+ createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS read: association specific named variable list %s access error: %i\n", listName, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
}
}
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */
diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c
index 3905b52d..b9d4c798 100644
--- a/src/mms/iso_mms/server/mms_server.c
+++ b/src/mms/iso_mms/server/mms_server.c
@@ -1,7 +1,7 @@
/*
* mms_server.c
*
- * Copyright 2013-2020 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -95,13 +95,13 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration)
if (self->valueCaches == NULL)
goto exit_error;
- self->isLocked = false;
-
self->transmitBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE);
if (self->transmitBuffer == NULL)
goto exit_error;
+ self->blockRequests = false;
+
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
self->fileServiceEnabled = true;
self->dynamicVariableListServiceEnabled = true;
@@ -359,10 +359,24 @@ MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connecti
}
void
-MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter)
+MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter)
+{
+ self->variableListAccessHandler = handler;
+ self->variableListAccessHandlerParameter = parameter;
+}
+
+void
+MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter)
{
- self->variableListChangedHandler = handler;
- self->variableListChangedHandlerParameter = parameter;
+ self->readJournalHandler = handler;
+ self->readJournalHandlerParameter = parameter;
+}
+
+void
+MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter)
+{
+ self->getNameListHandler = handler;
+ self->getNameListHandlerParameter = parameter;
}
void
@@ -565,7 +579,6 @@ exit_function:
return value;
}
-
MmsDevice*
MmsServer_getDevice(MmsServer self)
{
@@ -820,4 +833,8 @@ MmsServer_getFilesystemBasepath(MmsServer self)
#endif
}
-
+void
+MmsServer_ignoreClientRequests(MmsServer self, bool enable)
+{
+ self->blockRequests = enable;
+}
diff --git a/src/mms/iso_mms/server/mms_server_common.c b/src/mms/iso_mms/server/mms_server_common.c
index 62f9e0d5..455a26ec 100644
--- a/src/mms/iso_mms/server/mms_server_common.c
+++ b/src/mms/iso_mms/server/mms_server_common.c
@@ -391,7 +391,7 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char
LinkedList element = LinkedList_getNext(namedVariableLists);
- while (element != NULL) {
+ while (element) {
MmsNamedVariableList varList = (MmsNamedVariableList) element->data;
if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) {
@@ -405,14 +405,13 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char
return variableList;
}
-
void
mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName)
{
LinkedList previousElement = namedVariableLists;
LinkedList element = LinkedList_getNext(namedVariableLists);
- while (element != NULL ) {
+ while (element) {
MmsNamedVariableList varList = (MmsNamedVariableList) element->data;
if (strcmp(MmsNamedVariableList_getName(varList), variableListName)
@@ -428,5 +427,3 @@ mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListNa
element = LinkedList_getNext(element);
}
}
-
-
diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c
index b14ecc3a..4f914081 100644
--- a/src/mms/iso_mms/server/mms_server_connection.c
+++ b/src/mms/iso_mms/server/mms_server_connection.c
@@ -665,6 +665,9 @@ handleConfirmedResponsePdu(
static inline void
MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response)
{
+ if (self->server->blockRequests)
+ return;
+
uint8_t* buffer = message->buffer;
if (message->size < 2)
diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c
index 11306e4d..096bc30f 100644
--- a/src/mms/iso_mms/server/mms_write_service.c
+++ b/src/mms/iso_mms/server/mms_write_service.c
@@ -1,7 +1,7 @@
/*
* mms_write_service.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -430,8 +430,19 @@ handleWriteNamedVariableListRequest(
else {
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
- if (namedList != NULL) {
- createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ if (namedList) {
+
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_DOMAIN_SPECIFIC, domain, namedList->name, connection);
+
+ if (accessError == MMS_ERROR_NONE) {
+ createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s access error: %i\n", nameIdStr, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
+
}
else {
if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s not found!\n", nameIdStr);
@@ -448,8 +459,19 @@ handleWriteNamedVariableListRequest(
MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName);
- if (namedList != NULL) {
- createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ if (namedList) {
+
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_VMD_SPECIFIC, NULL, namedList->name, connection);
+
+ if (accessError == MMS_ERROR_NONE) {
+ createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s access error: %i\n", namedList->name, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
+
}
else {
if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s not found!\n", listName);
@@ -465,8 +487,19 @@ handleWriteNamedVariableListRequest(
MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName);
- if (namedList != NULL) {
- createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ if (namedList) {
+
+ MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection);
+
+ if (accessError == MMS_ERROR_NONE) {
+ createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
+ }
+ else {
+ if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s access error: %i\n", namedList->name, accessError);
+
+ mmsMsg_createServiceErrorPdu(invokeId, response, accessError);
+ }
+
}
else {
if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s not found!\n", listName);
@@ -479,6 +512,58 @@ handleWriteNamedVariableListRequest(
}
+static MmsVariableSpecification*
+getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, char* variableName)
+{
+ MmsVariableSpecification* retValue = NULL;
+
+ if (mmsServer_isComponentAccess(alternateAccess)) {
+ Identifier_t component =
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component;
+
+ if (component.size > 129)
+ goto exit_function;
+
+ if (namedVariable->type == MMS_STRUCTURE) {
+
+ int i;
+
+ for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) {
+
+ if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name)
+ == component.size) {
+ if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name,
+ (char*) component.buf, component.size))
+ {
+ if (strlen(variableName) + component.size < 199) {
+
+ StringUtils_appendString(variableName, 200, "$");
+
+ /* here we need strncat because component.buf is not null terminated! */
+ strncat(variableName, (const char*)component.buf, (size_t)component.size);
+
+ if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess
+ != NULL) {
+ retValue =
+ getComponent(connection, domain,
+ alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess,
+ namedVariable->typeSpec.structure.elements[i],
+ variableName);
+ }
+ else {
+ retValue = namedVariable->typeSpec.structure.elements[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+exit_function:
+ return retValue;
+}
+
void
mmsServer_handleWriteRequest(
MmsServerConnection connection,
@@ -603,15 +688,21 @@ mmsServer_handleWriteRequest(
AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
if (alternateAccess != NULL) {
- if (variable->type != MMS_ARRAY) {
+
+ if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue;
}
- if (!mmsServer_isIndexAccess(alternateAccess)) {
+ if ((variable->type == MMS_ARRAY) && (mmsServer_isIndexAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
continue;
}
+
+ if (variable->type != MMS_ARRAY && variable->type != MMS_STRUCTURE) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
+ continue;
+ }
}
Data_t* dataElement = writeRequest->listOfData.list.array[i];
@@ -628,72 +719,84 @@ mmsServer_handleWriteRequest(
if (domain == NULL)
domain = (MmsDomain*) device;
- MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
-
- if (cachedArray == NULL) {
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
- goto end_of_main_loop;
- }
-
- int index = mmsServer_getLowIndex(alternateAccess);
- int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
-
- if (numberOfElements == 0) { /* select single array element with index */
-
- MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
+ if (mmsServer_isIndexAccess(alternateAccess)) {
+ MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr);
- if (elementValue == NULL) {
+ if (cachedArray == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop;
}
- if (mmsServer_isAccessToArrayComponent(alternateAccess)) {
- MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr);
+ int index = mmsServer_getLowIndex(alternateAccess);
+ int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
- if (namedVariable) {
- elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue);
- }
+ if (numberOfElements == 0) { /* select single array element with index */
+
+ MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
- if ((namedVariable == NULL) || (elementValue == NULL)) {
- accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ if (elementValue == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop;
}
- }
- if (MmsValue_update(elementValue, value) == false) {
- accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
- goto end_of_main_loop;
- }
- }
- else { /* select sub-array with start-index and number-of-elements */
+ if (mmsServer_isAccessToArrayComponent(alternateAccess)) {
+ MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr);
- if (MmsValue_getType(value) != MMS_ARRAY) {
- accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
- goto end_of_main_loop;
- }
+ if (namedVariable) {
+ elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue);
+ }
- int elementNo;
-
- for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
- MmsValue* newElement = MmsValue_getElement(value, elementNo);
- MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
+ if ((namedVariable == NULL) || (elementValue == NULL)) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ goto end_of_main_loop;
+ }
+ }
- if ((elementValue == NULL) || (newElement == NULL) ) {
+ if (MmsValue_update(elementValue, value) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
+ }
+ else { /* select sub-array with start-index and number-of-elements */
- if (MmsValue_update(elementValue, newElement) == false) {
+ if (MmsValue_getType(value) != MMS_ARRAY) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop;
}
+ int elementNo;
+
+ for (elementNo = 0; elementNo < numberOfElements; elementNo++) {
+ MmsValue* newElement = MmsValue_getElement(value, elementNo);
+ MmsValue* elementValue = MmsValue_getElement(cachedArray, index++);
+
+ if ((elementValue == NULL) || (newElement == NULL) ) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+
+ if (MmsValue_update(elementValue, newElement) == false) {
+ accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
+ goto end_of_main_loop;
+ }
+ }
}
- }
- accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
- goto end_of_main_loop;
+ accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
+ goto end_of_main_loop;
+ }
+ else if (mmsServer_isComponentAccess(alternateAccess)) {
+ variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr);
+ if (variable == NULL) {
+ accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
+ goto end_of_main_loop;
+ }
+ }
+ else {
+ accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
+ goto end_of_main_loop;
+ }
}
/* Check for correct type */
@@ -723,7 +826,7 @@ mmsServer_handleWriteRequest(
goto exit_function;
}
- exit_function:
+exit_function:
MmsServer_unlockModel(connection->server);
diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c
index 7f997a9b..795b0636 100644
--- a/src/mms/iso_server/iso_connection.c
+++ b/src/mms/iso_server/iso_connection.c
@@ -1,7 +1,7 @@
/*
* iso_connection.c
*
- * Copyright 2013-2022 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
@@ -160,6 +160,8 @@ IsoConnection_removeFromHandleSet(const IsoConnection self, HandleSet handles)
void
IsoConnection_callTickHandler(IsoConnection self)
{
+ CotpConnection_flushBuffer(self->cotpConnection);
+
if (self->tickHandler) {
self->tickHandler(self->handlerParameter);
}
@@ -171,10 +173,7 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
#if (CONFIG_MMS_SINGLE_THREADED != 1)
if (isSingleThread == false) {
- /* call tick handler */
- if (self->tickHandler) {
- self->tickHandler(self->handlerParameter);
- }
+ IsoConnection_callTickHandler(self);
if (Handleset_waitReady(self->handleSet, 10) < 1)
goto exit_function;
diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c
index 62c43ebb..a2daa6ad 100644
--- a/src/mms/iso_server/iso_server.c
+++ b/src/mms/iso_server/iso_server.c
@@ -455,7 +455,6 @@ exit_function:
return success;
}
-
/** used by single and multi-threaded versions
*
* \param isSingleThread when true server is running in single thread or non-thread mode
diff --git a/src/r_session/r_session_crypto_mbedtls.c b/src/r_session/r_session_crypto_mbedtls.c
index 0fe577cb..761ebd59 100644
--- a/src/r_session/r_session_crypto_mbedtls.c
+++ b/src/r_session/r_session_crypto_mbedtls.c
@@ -3,7 +3,7 @@
*
* Implementation of RSessionCrypto interface using mbedtls
*
- * Copyright 2013-2021 Michael Zillgith
+ * Copyright 2013-2023 Michael Zillgith
*
* This file is part of libIEC61850.
*
diff --git a/third_party/mbedtls/README b/third_party/mbedtls/README
index 04597eb4..b6171f07 100644
--- a/third_party/mbedtls/README
+++ b/third_party/mbedtls/README
@@ -1,9 +1,9 @@
README
------
-For TLS support with mbedtls download the source tarball of version 2.16.x and extract here in the subfolder
+For TLS support with mbedtls download the source tarball of version 2.28.x and extract here in the subfolder
-mbedtls-2.16
+mbedtls-2.28
-After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.16". Otherwise the build system will not find the library.
+After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.28". Otherwise the build system will not find the library.