Merge branch 'v1.6_develop' into v1.6_develop_rgoose_sntp

v1.6_develop_rgoose_sntp
Michael Zillgith 2 years ago
commit da62fe854f

@ -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

@ -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_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) 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_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON)
option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" 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) 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_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.16/include" CACHE STRING "Path to search for the mbedtls include files" ) 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 # choose the library features which shall be included
option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control features" ON) 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_R_SMV "Build with support for R-SMV (mbedtls required)" ON)
option(CONFIG_IEC61850_SNTP_CLIENT "Build with SNTP client code" 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" ) set(CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE "65536" CACHE STRING "Default buffer size for buffered reports in byte" )
# advanced options # advanced options
@ -132,10 +137,15 @@ set(USE_PREBUILD_MBEDTLS 1)
set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH})
endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) 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(WITH_MBEDTLS 1)
set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include") 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.16) endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28)
if(WITH_MBEDTLS) if(WITH_MBEDTLS)
@ -162,6 +172,11 @@ if (SUPPORT_REDUNDANT_DECLS)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls")
endif(SUPPORT_REDUNDANT_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 # write the detected stuff to this file
configure_file( configure_file(
${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake ${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake

@ -78,9 +78,9 @@ LIB_INCLUDE_DIRS += third_party/winpcap/Include
endif endif
ifdef WITH_MBEDTLS 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_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 LIB_INCLUDE_DIRS += hal/tls/mbedtls
CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"' CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"'
CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1' CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1'

@ -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 ## 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 In the main libiec61850 folder run
@ -98,7 +98,7 @@ In the main libiec61850 folder run
make WITH_MBEDTLS=1 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 ## Installing the library and the API headers

@ -238,9 +238,6 @@
#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 #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 */ /* Support user access to raw messages */
#define CONFIG_MMS_RAW_MESSAGE_LOGGING 1 #define CONFIG_MMS_RAW_MESSAGE_LOGGING 1

@ -155,7 +155,7 @@
#cmakedefine01 CONFIG_IEC61850_SETTING_GROUPS #cmakedefine01 CONFIG_IEC61850_SETTING_GROUPS
/* default reservation time of a setting group control block in s */ /* 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 */ /* include support for IEC 61850 log services */
#cmakedefine01 CONFIG_IEC61850_LOG_SERVICE #cmakedefine01 CONFIG_IEC61850_LOG_SERVICE
@ -196,7 +196,10 @@
#define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10 #define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10
/* Maximum number of the members in a data set (named variable list) */ /* 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 */ /* Definition of supported services */
#define MMS_DEFAULT_PROFILE 1 #define MMS_DEFAULT_PROFILE 1
@ -224,9 +227,6 @@
#define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 #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 */ /* Support user acccess to raw messages */
#cmakedefine01 CONFIG_MMS_RAW_MESSAGE_LOGGING #cmakedefine01 CONFIG_MMS_RAW_MESSAGE_LOGGING

@ -775,7 +775,10 @@ namespace IEC61850
Dispose (true); Dispose (true);
} }
~ControlObject()
{
Dispose (false);
}
} }
} }

@ -1,7 +1,7 @@
/* /*
* IEC61850ClientAPI.cs * IEC61850ClientAPI.cs
* *
* Copyright 2014-2021 Michael Zillgith * Copyright 2014-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -437,6 +437,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort); 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)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedConnection_abort(IntPtr self, out int error); static extern void IedConnection_abort(IntPtr self, out int error);
@ -894,6 +897,25 @@ namespace IEC61850
Connect(hostname, -1); Connect(hostname, -1);
} }
/// <summary>
/// Set the local IP address and port to be used by the client
/// </summary>
/// <param name="localIpAddress">the local IP address or hostname</param>
/// <param name="localPort">the local TCP port to use. When 0 the OS will chose the TCP port to use.</param>
public void SetLocalAddress(string localIpAddress, int localPort)
{
IedConnection_setLocalAddress(connection, localIpAddress, localPort);
}
/// <summary>
/// Set the local IP address to be used by the client
/// </summary>
/// <param name="localIpAddress">the local IP address or hostname</param>
public void SetLocalAddress(string localIpAddress)
{
IedConnection_setLocalAddress(connection, localIpAddress, 0);
}
/// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception> /// <exception cref="IedConnectionException">This exception is thrown if there is a connection or service error</exception>
public ControlObject CreateControlObject(string objectReference) public ControlObject CreateControlObject(string objectReference)
{ {
@ -1993,7 +2015,6 @@ namespace IEC61850
if (accessResults != IntPtr.Zero) if (accessResults != IntPtr.Zero)
{ {
IntPtr element = LinkedList_getNext(accessResults); IntPtr element = LinkedList_getNext(accessResults);
while (element != IntPtr.Zero) while (element != IntPtr.Zero)
@ -2004,6 +2025,9 @@ namespace IEC61850
MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError(); MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError();
if (accessResultList == null)
accessResultList = new List<MmsDataAccessError>();
accessResultList.Add(dataAccessError); accessResultList.Add(dataAccessError);
element = LinkedList_getNext(element); element = LinkedList_getNext(element);

@ -1866,6 +1866,14 @@ namespace IEC61850
[return: MarshalAs(UnmanagedType.I1)] [return: MarshalAs(UnmanagedType.I1)]
static extern bool ControlAction_isSelect(IntPtr self); 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 IntPtr self;
private IedServer.ControlHandlerInfo info; private IedServer.ControlHandlerInfo info;
private IedServer iedServer; private IedServer iedServer;
@ -1985,6 +1993,16 @@ namespace IEC61850
{ {
return ControlAction_isSelect(self); 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); public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter);
@ -2273,6 +2291,9 @@ namespace IEC61850
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); 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 IntPtr self = IntPtr.Zero;
private InternalControlHandler internalControlHandlerRef = null; private InternalControlHandler internalControlHandlerRef = null;
@ -2435,6 +2456,9 @@ namespace IEC61850
/* store IedModel instance to prevent garbage collector */ /* store IedModel instance to prevent garbage collector */
private IedModel iedModel = null; private IedModel iedModel = null;
/* store TLSConfiguration instance to prevent garbage collector */
private TLSConfiguration tlsConfiguration = null;
public IedServer(IedModel iedModel, IedServerConfig config = null) public IedServer(IedModel iedModel, IedServerConfig config = null)
{ {
this.iedModel = iedModel; this.iedModel = iedModel;
@ -2450,6 +2474,7 @@ namespace IEC61850
public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null)
{ {
this.iedModel = iedModel; this.iedModel = iedModel;
this.tlsConfiguration = tlsConfig;
IntPtr nativeConfig = IntPtr.Zero; IntPtr nativeConfig = IntPtr.Zero;
IntPtr nativeTLSConfig = IntPtr.Zero; IntPtr nativeTLSConfig = IntPtr.Zero;
@ -2539,6 +2564,7 @@ namespace IEC61850
self = IntPtr.Zero; self = IntPtr.Zero;
internalConnectionHandler = null; internalConnectionHandler = null;
this.iedModel = null; this.iedModel = null;
this.tlsConfiguration = null;
} }
} }
} }
@ -2996,6 +3022,18 @@ namespace IEC61850
} }
} }
/// <summary>
/// Set the time quality for all timestamps internally generated by this IedServer instance
/// </summary>
/// <param name="leapSecondKnown">set/unset leap seconds known flag</param>
/// <param name="clockFailure">set/unset clock failure flag</param>
/// <param name="clockNotSynchronized">set/unset clock not synchronized flag</param>
/// <param name="subsecondPrecision">set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp)</param>
public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision)
{
IedServer_setTimeQuality(self, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision);
}
} }
} }

@ -1,7 +1,7 @@
/* /*
* TLS.cs * TLS.cs
* *
* Copyright 2017 Michael Zillgith * Copyright 2017-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -35,181 +35,400 @@ using IEC61850.Common;
/// </summary> /// </summary>
namespace IEC61850 namespace IEC61850
{ {
namespace TLS namespace TLS
{ {
/// <summary> public enum TLSConfigVersion
/// A container for TLS configuration and certificates. {
/// </summary> NOT_SELECTED = 0,
public class TLSConfiguration : IDisposable SSL_3_0 = 3,
{ TLS_1_0 = 4,
private IntPtr self = IntPtr.Zero; TLS_1_1 = 5,
TLS_1_2 = 6,
private bool allowOnlyKnownCerts = false; TLS_1_3 = 7
private bool chainValidation = true; }
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] public enum TLSEventLevel
static extern IntPtr TLSConfiguration_create(); {
INFO = 0,
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] WARNING = 1,
static extern void TLSConfiguration_destroy(IntPtr self); INCIDENT = 2
}
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); public enum TLSEventCode
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] ALM_ALGO_NOT_SUPPORTED = 1,
static extern void TLSConfiguration_setChainValidation (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); ALM_UNSECURE_COMMUNICATION = 2,
ALM_CERT_UNAVAILABLE = 3,
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] ALM_BAD_CERT = 4,
static extern void TLSConfiguration_setClientMode(IntPtr self); ALM_CERT_SIZE_EXCEEDED = 5,
ALM_CERT_VALIDATION_FAILED = 6,
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] ALM_CERT_REQUIRED = 7,
[return: MarshalAs(UnmanagedType.I1)] ALM_HANDSHAKE_FAILED_UNKNOWN_REASON = 8,
static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen); WRN_INSECURE_TLS_VERSION = 9,
INF_SESSION_RENEGOTIATION = 10,
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] ALM_CERT_EXPIRED = 11,
[return: MarshalAs(UnmanagedType.I1)] ALM_CERT_REVOKED = 12,
static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename); ALM_CERT_NOT_CONFIGURED = 13,
ALM_CERT_NOT_TRUSTED = 14
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] }
[return: MarshalAs(UnmanagedType.I1)]
static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword); public class TLSConnection
{
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)] static extern int TLSConnection_getTLSVersion(IntPtr self);
static extern bool TLSConfiguration_setOwnKeyFromFile (IntPtr self, string filename, string keyPassword);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern IntPtr TLSConnection_getPeerAddress(IntPtr self, IntPtr peerAddrBuf);
[return: MarshalAs(UnmanagedType.I1)]
static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen); [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr TLSConnection_getPeerCertificate(IntPtr self, out int certSize);
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.I1)] private IntPtr self;
static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename); private bool isValid;
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] internal TLSConnection(IntPtr self)
[return: MarshalAs(UnmanagedType.I1)] {
static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen); this.self = self;
isValid = true;
[DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] }
[return: MarshalAs(UnmanagedType.I1)]
static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename); // To be called by event callback caller after callback execution
internal void InValidate()
public TLSConfiguration() { {
self = TLSConfiguration_create (); lock (this)
} {
isValid = false;
~TLSConfiguration() }
{ }
Dispose ();
} /// <summary>
/// TLS version used by the connection
internal IntPtr GetNativeInstance() /// </summary>
{ public TLSConfigVersion TLSVersion
return self; {
} get
{
public bool AllowOnlyKnownCertificates lock (this)
{ {
set { if (isValid)
TLSConfiguration_setAllowOnlyKnownCertificates (self, value); {
allowOnlyKnownCerts = value; return (TLSConfigVersion)TLSConnection_getTLSVersion((IntPtr)self);
} }
get { else
return allowOnlyKnownCerts; {
} throw new InvalidOperationException("Object cannot be used outside of TLS event callback");
} }
}
public bool ChainValidation }
{ }
set {
TLSConfiguration_setChainValidation (self, value); /// <summary>
chainValidation = value; /// Peer IP address and TCP port of the TLS connection
} /// </summary>
get { public string PeerAddress
return chainValidation; {
} get
} {
lock (this)
{
public void SetClientMode() if (isValid)
{ {
TLSConfiguration_setClientMode (self); IntPtr peerAddrBuf = Marshal.AllocHGlobal(130);
} IntPtr peerAddrStr = TLSConnection_getPeerAddress(this.self, peerAddrBuf);
public void SetOwnCertificate(string filename) string peerAddr = null;
{
if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) { if (peerAddrStr != IntPtr.Zero)
throw new CryptographicException ("Failed to read certificate from file"); {
} peerAddr = Marshal.PtrToStringAnsi(peerAddrStr);
} }
public void SetOwnCertificate(X509Certificate2 cert) Marshal.FreeHGlobal(peerAddrBuf);
{
byte[] certBytes = cert.GetRawCertData (); return peerAddr;
}
if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) { else
throw new CryptographicException ("Failed to set certificate"); {
} throw new InvalidOperationException("Object cannot be used outside of TLS event callback");
} }
}
public void AddAllowedCertificate(string filename) }
{ }
if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) {
throw new CryptographicException ("Failed to read allowed certificate from file"); /// <summary>
} /// TLS certificate used by the peer
} /// </summary>
public byte[] PeerCertificate
public void AddAllowedCertificate(X509Certificate2 cert) {
{ get
byte[] certBytes = cert.GetRawCertData (); {
lock (this)
if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) { {
throw new CryptographicException ("Failed to add allowed certificate"); if (isValid)
} {
} int certSize;
public void AddCACertificate(string filename) IntPtr certBuffer = TLSConnection_getPeerCertificate(self, out certSize);
{
if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) { if (certBuffer != IntPtr.Zero)
throw new CryptographicException ("Failed to read CA certificate from file"); {
} if (certSize > 0)
} {
byte[] cert = new byte[certSize];
public void AddCACertificate(X509Certificate2 cert)
{ Marshal.Copy(certBuffer, cert, 0, certSize);
byte[] certBytes = cert.GetRawCertData ();
return cert;
if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) { }
throw new CryptographicException ("Failed to add CA certificate"); }
}
} return null;
}
public void SetOwnKey (string filename, string password) else
{ {
if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) { throw new InvalidOperationException("Object cannot be used outside of TLS event callback");
throw new CryptographicException ("Failed to read own key from file"); }
} }
} }
}
public void SetOwnKey (X509Certificate2 key, string password)
{ }
byte[] certBytes = key.Export (X509ContentType.Pkcs12);
/// <summary>
if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) { /// TLS security event handler
throw new CryptographicException ("Failed to set own key"); /// </summary>
} /// <param name="parameter">user provided context paramter to be passed to the handler</param>
} /// <param name="eventLevel">severity level of the event</param>
/// <param name="eventCode">code to identify the event type</param>
public void Dispose() /// <param name="message">text message describing the event</param>
{ /// <param name="connection">TLS connection that caused the event</param>
lock (this) { public delegate void TLSEventHandler(object parameter, TLSEventLevel eventLevel, TLSEventCode eventCode, string message, TLSConnection connection);
if (self != IntPtr.Zero) {
TLSConfiguration_destroy (self); /// <summary>
self = IntPtr.Zero; /// A container for TLS configuration and certificates.
} /// </summary>
} 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;
}
}
}
}
}
}

@ -14,6 +14,7 @@ add_subdirectory(server_example_files)
add_subdirectory(server_example_substitution) add_subdirectory(server_example_substitution)
add_subdirectory(server_example_service_tracking) add_subdirectory(server_example_service_tracking)
add_subdirectory(server_example_deadband) add_subdirectory(server_example_deadband)
add_subdirectory(server_example_access_control)
add_subdirectory(iec61850_client_example1) add_subdirectory(iec61850_client_example1)
add_subdirectory(iec61850_client_example2) add_subdirectory(iec61850_client_example2)

@ -33,6 +33,9 @@ int main(int argc, char** argv) {
char* hostname; char* hostname;
int tcpPort = 102; int tcpPort = 102;
const char* localIp = NULL;
int localTcpPort = -1;
if (argc > 1) if (argc > 1)
hostname = argv[1]; hostname = argv[1];
@ -42,19 +45,34 @@ int main(int argc, char** argv) {
if (argc > 2) if (argc > 2)
tcpPort = atoi(argv[2]); tcpPort = atoi(argv[2]);
if (argc > 3)
localIp = argv[3];
if (argc > 4)
localTcpPort = atoi(argv[4]);
IedClientError error; IedClientError error;
IedConnection con = IedConnection_create(); 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); 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 */ /* read an analog measurement value from server */
MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX); 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) { if (MmsValue_getType(value) == MMS_FLOAT) {
float fval = MmsValue_toFloat(value); float fval = MmsValue_toFloat(value);
printf("read float value: %f\n", fval); printf("read float value: %f\n", fval);
@ -137,7 +155,6 @@ close_connection:
} }
IedConnection_destroy(con); IedConnection_destroy(con);
return 0; return 0;
} }

@ -45,8 +45,10 @@ int main(int argc, char** argv) {
IedConnection_connect(con, &error, hostname, tcpPort); IedConnection_connect(con, &error, hostname, tcpPort);
if (error == IED_ERROR_OK) { if (error == IED_ERROR_OK)
{
MmsValue* ctlVal = NULL;
MmsValue* stVal = NULL;
/************************ /************************
* Direct control * Direct control
@ -55,99 +57,116 @@ int main(int argc, char** argv) {
ControlObjectClient control ControlObjectClient control
= ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con); = 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 */)) { if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n"); printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n");
} }
else { else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n"); 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) { printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
bool state = MmsValue_getBoolean(stVal); }
MmsValue_delete(stVal); else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n");
}
printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state);
} }
else { else {
printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n"); printf("Control object simpleIOGenericIO/GGIO1.SPCSO1 not found in server\n");
} }
/************************ /************************
* Select before operate * Select before operate
***********************/ ***********************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO2", con); 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 */)) { if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n"); printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n");
}
MmsValue_delete(ctlVal);
} }
else { else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n"); printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n");
} }
MmsValue_delete(ctlVal); ControlObjectClient_destroy(control);
} }
else { 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 * Direct control with enhanced security
****************************************/ ****************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con); 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 */)) { if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) {
printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n"); printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n");
} }
else { else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n"); printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n");
} }
MmsValue_delete(ctlVal); MmsValue_delete(ctlVal);
/* Wait for command termination message */ /* Wait for command termination message */
Thread_sleep(1000); 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) { if (error == IED_ERROR_OK) {
bool state = MmsValue_getBoolean(stVal); 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 { 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); 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 { 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 { 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-) * Direct control with enhanced security (expect CommandTermination-)
*********************************************************************/ *********************************************************************/
control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con); control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con);
ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); if (control)
{
ctlVal = MmsValue_newBoolean(true); ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL);
if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { ctlVal = MmsValue_newBoolean(true);
printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n");
}
else {
printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n");
}
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 */ MmsValue_delete(ctlVal);
Thread_sleep(1000);
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); IedConnection_close(con);
} }

@ -101,6 +101,7 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
int returnCode = 0;
char* hostname = StringUtils_copyString("localhost"); char* hostname = StringUtils_copyString("localhost");
int tcpPort = 102; int tcpPort = 102;
@ -213,6 +214,10 @@ int main(int argc, char** argv)
if (!MmsConnection_connect(con, &error, hostname, tcpPort)) { if (!MmsConnection_connect(con, &error, hostname, tcpPort)) {
printf("MMS connect failed!\n"); printf("MMS connect failed!\n");
if (error != MMS_ERROR_NONE)
returnCode = error;
goto exit; goto exit;
} }
else else
@ -222,6 +227,9 @@ int main(int argc, char** argv)
MmsServerIdentity* identity = MmsServerIdentity* identity =
MmsConnection_identify(con, &error); MmsConnection_identify(con, &error);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (identity != NULL) { if (identity != NULL) {
printf("\nServer identity:\n----------------\n"); printf("\nServer identity:\n----------------\n");
printf(" vendor:\t%s\n", identity->vendorName); printf(" vendor:\t%s\n", identity->vendorName);
@ -235,14 +243,23 @@ int main(int argc, char** argv)
if (readDeviceList) { if (readDeviceList) {
printf("\nDomains present on server:\n--------------------------\n"); printf("\nDomains present on server:\n--------------------------\n");
LinkedList nameList = MmsConnection_getDomainNames(con, &error); 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) { if (getDeviceDirectory) {
LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error, LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error,
domainName); domainName);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (variableList) { if (variableList) {
LinkedList element = LinkedList_getNext(variableList); LinkedList element = LinkedList_getNext(variableList);
@ -264,6 +281,9 @@ int main(int argc, char** argv)
variableList = MmsConnection_getDomainJournals(con, &error, domainName); variableList = MmsConnection_getDomainJournals(con, &error, domainName);
if (error != MMS_ERROR_NONE)
returnCode = error;
if (variableList) { if (variableList) {
LinkedList element = variableList; LinkedList element = variableList;
@ -309,6 +329,9 @@ int main(int argc, char** argv)
LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime, LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime,
&moreFollows); &moreFollows);
if (error != MMS_ERROR_NONE)
returnCode = error;
MmsValue_delete(startTime); MmsValue_delete(startTime);
MmsValue_delete(endTime); MmsValue_delete(endTime);
@ -375,6 +398,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) { if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error); printf("Reading variable failed: (ERROR %i)\n", error);
returnCode = error;
} }
else { else {
printf("Read SUCCESS\n"); printf("Read SUCCESS\n");
@ -403,6 +428,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) { if (error != MMS_ERROR_NONE) {
printf("Reading variable failed: (ERROR %i)\n", error); printf("Reading variable failed: (ERROR %i)\n", error);
returnCode = error;
} }
else { else {
printf("Read SUCCESS\n"); printf("Read SUCCESS\n");
@ -421,6 +448,8 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) { if (error != MMS_ERROR_NONE) {
printf("Reading variable list directory failed: (ERROR %i)\n", error); printf("Reading variable list directory failed: (ERROR %i)\n", error);
returnCode = error;
} }
else { else {
LinkedList varListElem = LinkedList_getNext(varListDir); LinkedList varListElem = LinkedList_getNext(varListDir);
@ -454,12 +483,19 @@ int main(int argc, char** argv)
char* continueAfter = NULL; char* continueAfter = NULL;
while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) { while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) {
if (error != MMS_ERROR_NONE)
returnCode = error;
continueAfter = lastName; continueAfter = lastName;
} }
} }
if (getFileAttributes) { if (getFileAttributes) {
MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL); MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL);
if (error != MMS_ERROR_NONE)
returnCode = error;
} }
if (deleteFile) { if (deleteFile) {
@ -467,13 +503,14 @@ int main(int argc, char** argv)
if (error != MMS_ERROR_NONE) { if (error != MMS_ERROR_NONE) {
printf("Delete file failed: (ERROR %i)\n", error); printf("Delete file failed: (ERROR %i)\n", error);
returnCode = error;
} }
else { else {
printf("File deleted\n"); printf("File deleted\n");
} }
} }
exit: exit:
free(hostname); free(hostname);
free(domainName); free(domainName);
free(variableName); free(variableName);
@ -482,6 +519,6 @@ int main(int argc, char** argv)
MmsConnection_destroy(con); MmsConnection_destroy(con);
return 0; return returnCode;
} }

@ -102,7 +102,3 @@ main(int argc, char **argv)
return 0; return 0;
} }

@ -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
)

@ -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

@ -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 <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#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() */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,311 @@
/*
* static_model.h
*
* automatically generated from simpleIO_direct_control.cid
*/
#ifndef STATIC_MODEL_H_
#define STATIC_MODEL_H_
#include <stdlib.h>
#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_ */

@ -100,6 +100,12 @@ rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conne
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create new server configuration object */ /* Create new server configuration object */
@ -163,7 +169,7 @@ main(int argc, char** argv)
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW); IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW);
/* MMS server will be instructed to start listening for client connections. */ /* MMS server will be instructed to start listening for client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n");

@ -126,6 +126,11 @@ int
main(int argc, char** argv) main(int argc, char** argv)
{ {
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1,
(ControlHandler) controlHandlerForBinaryOutput, (ControlHandler) controlHandlerForBinaryOutput,
@ -169,7 +174,7 @@ main(int argc, char** argv)
IEDMODEL_GenericIO_GGIO1_SPCSO9); IEDMODEL_GenericIO_GGIO1_SPCSO9);
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -36,6 +36,12 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected,
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create new server configuration object */ /* Create new server configuration object */
@ -74,7 +80,7 @@ main(int argc, char** argv)
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW); IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW);
/* MMS server will be instructed to start listening for client connections. */ /* MMS server will be instructed to start listening for client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n");

@ -55,6 +55,12 @@ fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServi
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
@ -70,7 +76,7 @@ main(int argc, char** argv)
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -115,9 +115,37 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in
return true; 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 int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
@ -141,6 +169,10 @@ main(int argc, char** argv)
IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL);
IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL);
IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL);
LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db");
LogStorage_setMaxLogEntries(statusLog, 10); LogStorage_setMaxLogEntries(statusLog, 10);
@ -174,7 +206,7 @@ main(int argc, char** argv)
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -159,6 +159,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu
int main(int argc, char** argv) { int main(int argc, char** argv) {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
/* Activate authentication */ /* Activate authentication */
@ -194,7 +200,7 @@ int main(int argc, char** argv) {
IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -97,7 +97,10 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu
{ {
void* securityToken = ClientConnection_getSecurityToken(connection); 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; return DATA_ACCESS_ERROR_SUCCESS;
} }
@ -105,6 +108,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
IedServerConfig config = IedServerConfig_create(); IedServerConfig config = IedServerConfig_create();
//IedServerConfig_enableEditSG(config, false); //IedServerConfig_enableEditSG(config, false);
@ -112,6 +121,8 @@ main(int argc, char** argv)
iedServer = IedServer_createWithConfig(&iedModel, NULL, config); iedServer = IedServer_createWithConfig(&iedModel, NULL, config);
IedServer_setTimeQuality(iedServer, true, false, false, 10);
IedServerConfig_destroy(config); IedServerConfig_destroy(config);
LogicalDevice* ld = IEDMODEL_PROT; LogicalDevice* ld = IEDMODEL_PROT;
@ -127,7 +138,7 @@ main(int argc, char** argv)
IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL);
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -200,6 +200,12 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString());
/* Create a new IEC 61850 server instance */ /* 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); IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_blkEna, writeAccessHandler, NULL);
/* MMS server will be instructed to start listening for client connections. */ /* MMS server will be instructed to start listening for client connections. */
IedServer_start(iedServer, 102); IedServer_start(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -65,6 +65,11 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
@ -86,7 +91,7 @@ main(int argc, char** argv)
IEDMODEL_GenericIO_GGIO1_SPCSO4); IEDMODEL_GenericIO_GGIO1_SPCSO4);
/* MMS server will be instructed to start listening to client connections. */ /* MMS server will be instructed to start listening to client connections. */
IedServer_startThreadless(iedServer, 102); IedServer_startThreadless(iedServer, tcpPort);
if (!IedServer_isRunning(iedServer)) { if (!IedServer_isRunning(iedServer)) {
printf("Starting server failed! Exit.\n"); printf("Starting server failed! Exit.\n");

@ -41,10 +41,16 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect
int int
main(int argc, char** argv) main(int argc, char** argv)
{ {
int tcpPort = 102;
if (argc > 1) {
tcpPort = atoi(argv[1]);
}
iedServer = IedServer_create(&iedModel); iedServer = IedServer_create(&iedModel);
/* MMS server will be instructed to start listening to client connections. */ /* 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 */ /* Don't allow access to SP variables by default */
IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_SP, ACCESS_POLICY_DENY); IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_SP, ACCESS_POLICY_DENY);

@ -30,11 +30,15 @@ reportCallbackFunction(void* parameter, ClientReport report)
} }
static void 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; (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) { int main(int argc, char** argv) {

@ -103,11 +103,17 @@ clientAuthenticator(void* parameter, AcseAuthenticationParameter authParameter,
} }
static void 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; (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 int

@ -10,7 +10,7 @@ endif()
project(hal) project(hal)
set(LIBHAL_VERSION_MAJOR "2") set(LIBHAL_VERSION_MAJOR "2")
set(LIBHAL_VERSION_MINOR "0") set(LIBHAL_VERSION_MINOR "1")
set(LIBHAL_VERSION_PATCH "0") set(LIBHAL_VERSION_PATCH "0")
# feature checks # feature checks
@ -129,7 +129,7 @@ include_directories(
if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH}) link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH})
else() 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) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB)
add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h")

@ -263,6 +263,8 @@ void
Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress) Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress)
{ {
struct packet_mreq mreq; struct packet_mreq mreq;
memset(&mreq, 0, sizeof(struct packet_mreq));
mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex; mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex;
mreq.mr_alen = ETH_ALEN; mreq.mr_alen = ETH_ALEN;
mreq.mr_type = PACKET_MR_MULTICAST; mreq.mr_type = PACKET_MR_MULTICAST;

@ -357,7 +357,7 @@ Socket_getPeerAddress(Socket self);
* *
* The peer address has to be returned as null terminated string * 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 self the client, connection or server socket instance
* \param peerAddressString a string to store the peer address (the string should have space * \param peerAddressString a string to store the peer address (the string should have space

@ -3,7 +3,7 @@
* *
* TLS Configuration API for protocol stacks using TCP/IP * 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 * Abstraction layer for configuration of different TLS implementations
* *
@ -50,11 +50,30 @@ TLSConfiguration_create(void);
PAL_API void PAL_API void
TLSConfiguration_setClientMode(TLSConfiguration self); 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 { typedef enum {
TLS_SEC_EVT_INFO = 0, TLS_SEC_EVT_INFO = 0,
TLS_SEC_EVT_WARNING = 1, TLS_SEC_EVT_WARNING = 1,
TLS_SEC_EVT_INCIDENT = 2 TLS_SEC_EVT_INCIDENT = 2
} TLSConfiguration_EventLevel; } TLSEventLevel;
#define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1 #define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1
#define TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2 #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_EXPIRED 11
#define TLS_EVENT_CODE_ALM_CERT_REVOKED 12 #define TLS_EVENT_CODE_ALM_CERT_REVOKED 12
#define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13 #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 * \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 PAL_API void
TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter); TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter);
@ -209,15 +266,6 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
PAL_API void PAL_API void
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs); 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 * \brief Set minimal allowed TLS version to use
*/ */
@ -249,6 +297,12 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen);
PAL_API bool PAL_API bool
TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename); 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 * Release all resource allocated by the TLSConfiguration instance
* *

@ -38,7 +38,6 @@ Memory_malloc(size_t size)
return memory; return memory;
} }
void* void*
Memory_calloc(size_t nmemb, size_t size) Memory_calloc(size_t nmemb, size_t size)
{ {
@ -50,7 +49,6 @@ Memory_calloc(size_t nmemb, size_t size)
return memory; return memory;
} }
void * void *
Memory_realloc(void *ptr, size_t size) Memory_realloc(void *ptr, size_t size)
{ {
@ -67,4 +65,3 @@ Memory_free(void* memb)
{ {
free(memb); free(memb);
} }

@ -105,10 +105,10 @@ struct sTLSSocket {
}; };
static void 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) { 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) if (certMatches)
*flags = 0; *flags = 0;
else { 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; *flags |= MBEDTLS_X509_BADCERT_OTHER;
return 1; return 1;
@ -214,7 +214,7 @@ TLSConfiguration_setupComplete(TLSConfiguration self)
int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey)); int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey));
if (ret != 0) { 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; return false;
} }
} }
@ -265,7 +265,7 @@ TLSConfiguration_create()
mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED); 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}; */ /* static int hashes[] = {3,4,5,6,7,8,0}; */
/* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */ /* 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); int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen);
if (ret != 0) 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); return (ret == 0);
} }
@ -360,7 +360,7 @@ TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* fi
int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename); int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename);
if (ret != 0) 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); 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)); int ret = mbedtls_pk_parse_key(&(self->ownKey), key, keyLen, (const uint8_t*) keyPassword, (keyPassword == NULL) ? 0 : strlen(keyPassword));
if (ret != 0) 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); return (ret == 0);
} }
@ -382,7 +382,7 @@ TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename,
int ret = mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword); int ret = mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword);
if (ret != 0) 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); 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); int ret = mbedtls_x509_crt_parse(&(self->cacerts), certificate, certLen);
if (ret != 0) { 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; return false;
} }
@ -440,21 +440,41 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil
int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename); int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename);
if (ret != 0) 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); 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 bool
TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen)
{ {
int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen); int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen);
if (ret != 0) { 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 { else {
self->crlUpdated = Hal_getTimeInMs(); udpatedCRL(self);
} }
return (ret == 0); 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); DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret);
} }
else { else {
self->crlUpdated = Hal_getTimeInMs(); udpatedCRL(self);
} }
return (ret == 0); 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 void
TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs)
{ {
@ -518,63 +546,72 @@ TLSConfiguration_destroy(TLSConfiguration self)
} }
static void static void
createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags) createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket socket)
{ {
if (config->eventHandler == NULL) if (config->eventHandler == NULL)
return; return;
switch (ret) { 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: 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; break;
case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION: 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; break;
case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: 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; break;
case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE: 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; break;
case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE: 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; break;
case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED: 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; break;
case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED: 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; break;
case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
{ {
if (flags & MBEDTLS_X509_BADCERT_EXPIRED) { 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) { 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) { 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) { 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 { else if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) {
raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: Certificate verification failed"); 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; break;
default: 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; 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 ) 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)); uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
createSecurityEvents(configuration, ret, flags); createSecurityEvents(configuration, ret, flags, self);
mbedtls_ssl_free(&(self->ssl)); mbedtls_ssl_free(&(self->ssl));
@ -793,7 +830,7 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient
self->lastRenegotiationTime = Hal_getTimeInMs(); self->lastRenegotiationTime = Hal_getTimeInMs();
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { 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 (ret == 0) {
if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { 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; return true;
@ -828,38 +865,59 @@ TLSSocket_performHandshake(TLSSocket self)
if (self->tlsConfig->eventHandler) { if (self->tlsConfig->eventHandler) {
uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
createSecurityEvents(self->tlsConfig, ret, flags); createSecurityEvents(self->tlsConfig, ret, flags, self);
} }
return false; return false;
} }
} }
int static void
TLSSocket_read(TLSSocket self, uint8_t* buf, int size) checkForCRLUpdate(TLSSocket self)
{ {
if (self->crlUpdated != self->tlsConfig->crlUpdated) { if (self->crlUpdated == self->tlsConfig->crlUpdated)
DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); 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) { DEBUG_PRINT("TLS", " started renegotiation\n");
if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { 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) { if (startRenegotiationIfRequired(self) == false) {
DEBUG_PRINT("TLS", " renegotiation failed\n"); return -1;
return -1;
}
else {
DEBUG_PRINT("TLS", " started renegotiation\n");
self->lastRenegotiationTime = Hal_getTimeInMs();
}
}
} }
int ret = mbedtls_ssl_read(&(self->ssl), buf, size); 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)); uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl));
createSecurityEvents(self->tlsConfig, ret, flags); createSecurityEvents(self->tlsConfig, ret, flags, self);
} }
return -1; return -1;
@ -901,28 +959,10 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size)
int ret; int ret;
int len = size; int len = size;
if (self->crlUpdated != self->tlsConfig->crlUpdated) { checkForCRLUpdate(self);
DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n");
mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) );
self->crlUpdated = self->tlsConfig->crlUpdated; if (startRenegotiationIfRequired(self) == false) {
} return -1;
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();
}
}
} }
while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0) 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)) 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; break;
} }
} }
@ -970,3 +1010,54 @@ TLSSocket_close(TLSSocket self)
GLOBAL_FREEMEM(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";
}
}

@ -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)

@ -1,7 +1,7 @@
if(WITH_MBEDTLS) if(WITH_MBEDTLS)
include_directories( include_directories(
${CMAKE_CURRENT_LIST_DIR}/tls/mbedtls ${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) endif(WITH_MBEDTLS)

@ -197,6 +197,9 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1)
{ {
char* res = dest; char* res = dest;
if (maxBufferSize < 1)
return NULL;
if (dest == NULL) if (dest == NULL)
res = (char*)GLOBAL_MALLOC(maxBufferSize); res = (char*)GLOBAL_MALLOC(maxBufferSize);

@ -234,13 +234,22 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case 0x84: /* BIT STRING */ case 0x84: /* BIT STRING */
if (MmsValue_getType(value) == MMS_BIT_STRING) { if (MmsValue_getType(value) == MMS_BIT_STRING) {
int padding = buffer[bufPos]; int padding = buffer[bufPos];
int bitStringLength = (8 * (elementLength - 1)) - padding;
if (bitStringLength == value->value.bitString.size) { if (padding > 7) {
memcpy(value->value.bitString.buf, buffer + bufPos + 1, if (DEBUG_GOOSE_SUBSCRIBER)
elementLength - 1); printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n");
pe = GOOSE_PARSE_ERROR_INVALID_PADDING;
} }
else { 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 { else {
@ -376,7 +385,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
break; break;
} }
if ( pe != GOOSE_PARSE_ERROR_NO_ERROR ) { if (pe != GOOSE_PARSE_ERROR_NO_ERROR) {
break; /* from while */ break; /* from while */
} }
@ -386,14 +395,16 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
} }
if (elementIndex <= maxIndex) { if (elementIndex <= maxIndex) {
pe = GOOSE_PARSE_ERROR_UNDERFLOW; if (pe == GOOSE_PARSE_ERROR_NO_ERROR) {
pe = GOOSE_PARSE_ERROR_UNDERFLOW;
}
} }
if (DEBUG_GOOSE_SUBSCRIBER) { if (DEBUG_GOOSE_SUBSCRIBER) {
switch ( pe ) { switch (pe) {
case GOOSE_PARSE_ERROR_UNKNOWN_TAG: case GOOSE_PARSE_ERROR_UNKNOWN_TAG:
printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag); printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag);
break; break;
case GOOSE_PARSE_ERROR_TAGDECODE: case GOOSE_PARSE_ERROR_TAGDECODE:
printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n");
break; break;
@ -412,6 +423,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues)
case GOOSE_PARSE_ERROR_LENGTH_MISMATCH: case GOOSE_PARSE_ERROR_LENGTH_MISMATCH:
printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n"); printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n");
break; break;
case GOOSE_PARSE_ERROR_INVALID_PADDING:
printf("GOOSE_SUBSCRIBER: Malformed message: invalid padding!\n");
default: default:
break; break;
} }
@ -524,7 +537,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt
case 0x83: /* boolean */ case 0x83: /* boolean */
if (DEBUG_GOOSE_SUBSCRIBER) if (DEBUG_GOOSE_SUBSCRIBER)
printf("GOOSE_SUBSCRIBER: found boolean\n"); 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; break;

@ -47,6 +47,7 @@ typedef enum
GOOSE_PARSE_ERROR_UNDERFLOW, GOOSE_PARSE_ERROR_UNDERFLOW,
GOOSE_PARSE_ERROR_TYPE_MISMATCH, GOOSE_PARSE_ERROR_TYPE_MISMATCH,
GOOSE_PARSE_ERROR_LENGTH_MISMATCH, GOOSE_PARSE_ERROR_LENGTH_MISMATCH,
GOOSE_PARSE_ERROR_INVALID_PADDING
} GooseParseError; } GooseParseError;
typedef struct sGooseSubscriber* GooseSubscriber; typedef struct sGooseSubscriber* GooseSubscriber;

@ -29,7 +29,7 @@
#include "mms_client_connection.h" #include "mms_client_connection.h"
#include "ied_connection_private.h" #include "ied_connection_private.h"
#if _MSC_VER #ifdef _MSC_VER
#define snprintf _snprintf #define snprintf _snprintf
#endif #endif

@ -139,7 +139,7 @@ ClientReport_getRptId(ClientReport self)
ReasonForInclusion ReasonForInclusion
ClientReport_getReasonForInclusion(ClientReport self, int elementIndex) ClientReport_getReasonForInclusion(ClientReport self, int elementIndex)
{ {
if (self->reasonForInclusion != NULL) if ((self->reasonForInclusion != NULL) && (elementIndex < self->dataSetSize) && (elementIndex >= 0))
return self->reasonForInclusion[elementIndex]; return self->reasonForInclusion[elementIndex];
else else
return IEC61850_REASON_NOT_INCLUDED; return IEC61850_REASON_NOT_INCLUDED;

@ -808,8 +808,9 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD
handler(param->originalInvokeId, call->callbackParameter, err); handler(param->originalInvokeId, call->callbackParameter, err);
releaseWriteCall(self, call, param); releaseWriteCall(self, call, param);
}
goto exit_function;
}
param->currentItemId = LinkedList_getNext(param->currentItemId); 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); MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &writeError, param->domainId, itemId, value, writeVariableHandler, self);
if (writeError != MMS_ERROR_NONE) { if (writeError != MMS_ERROR_NONE) {
handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError)); handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError));
releaseWriteCall(self, call, param); releaseWriteCall(self, call, param);
@ -839,6 +841,10 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD
if (DEBUG_IED_CLIENT) if (DEBUG_IED_CLIENT)
printf("IED_CLIENT: internal error - no matching outstanding call!\n"); printf("IED_CLIENT: internal error - no matching outstanding call!\n");
} }
exit_function:
return;
} }
uint32_t uint32_t

@ -105,6 +105,9 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError)
case MMS_ERROR_DEFINITION_OBJECT_UNDEFINED: case MMS_ERROR_DEFINITION_OBJECT_UNDEFINED:
return IED_ERROR_OBJECT_UNDEFINED; return IED_ERROR_OBJECT_UNDEFINED;
case MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE:
return IED_ERROR_TEMPORARILY_UNAVAILABLE;
default: default:
return IED_ERROR_UNKNOWN; return IED_ERROR_UNKNOWN;
} }
@ -648,6 +651,15 @@ IedConnection_tick(IedConnection self)
return MmsConnection_tick(self->connection); 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 void
IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs) IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs)
{ {
@ -1745,7 +1757,7 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error
LinkedList_destroy(logicalDeviceNames); LinkedList_destroy(logicalDeviceNames);
} }
else else
*error = iedConnection_mapMmsErrorToIedError(mmsError); if (error) *error = iedConnection_mapMmsErrorToIedError(mmsError);
} }
LinkedList /*<char*>*/ LinkedList /*<char*>*/
@ -3294,7 +3306,18 @@ uint32_t
IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements,
IedConnection_GenericServiceHandler handler, void* parameter) 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 domainIdBuffer[65];
char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1];
@ -3338,19 +3361,6 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons
isAssociationSpecific = true; 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 dataSetEntries = LinkedList_create();
LinkedList dataSetElement = LinkedList_getNext(dataSetElements); LinkedList dataSetElement = LinkedList_getNext(dataSetElements);
@ -3379,8 +3389,6 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons
&mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self); &mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self);
} }
invokeId = call->invokeId;
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
cleanup_list: cleanup_list:
@ -3388,7 +3396,15 @@ cleanup_list:
LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy); LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy);
exit_function: exit_function:
return invokeId;
if (*error != IED_ERROR_OK) {
iedConnection_releaseOutstandingCall(self, call);
return 0;
}
else {
return call->invokeId;
}
} }
LinkedList /* <char*> */ LinkedList /* <char*> */
@ -3513,6 +3529,8 @@ getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mms
if (handler) if (handler)
handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable); handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable);
iedConnection_releaseOutstandingCall(self, call);
} }
if (specs) if (specs)
@ -3523,7 +3541,7 @@ uint32_t
IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference, IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference,
IedConnection_GetDataSetDirectoryHandler handler, void* parameter) IedConnection_GetDataSetDirectoryHandler handler, void* parameter)
{ {
uint32_t invokeId = 0; MmsError mmsError = MMS_ERROR_NONE;
IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self);
@ -3578,9 +3596,6 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error
isAssociationSpecific = true; isAssociationSpecific = true;
} }
MmsError mmsError = MMS_ERROR_NONE;
if (isAssociationSpecific) if (isAssociationSpecific)
MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self); MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self);
@ -3590,7 +3605,15 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error
*error = iedConnection_mapMmsErrorToIedError(mmsError); *error = iedConnection_mapMmsErrorToIedError(mmsError);
exit_function: exit_function:
return invokeId;
if (*error != IED_ERROR_OK) {
iedConnection_releaseOutstandingCall(self, call);
return 0;
}
else {
return call->invokeId;
}
} }
ClientDataSet ClientDataSet

@ -231,6 +231,17 @@ IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig);
LIB61850_API void LIB61850_API void
IedConnection_destroy(IedConnection self); 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 * \brief set the connect timeout in ms

@ -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 */ /** FCs (Functional constraints) according to IEC 61850-7-2 */
typedef enum eFunctionalConstraint { typedef enum eFunctionalConstraint {
/** Status information */ /** Status information */

@ -395,6 +395,12 @@ LIB61850_API LogControlBlock*
LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps, LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps,
uint32_t intgPd, bool logEna, bool reasonCode); 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) * \brief create a log (used by the IEC 61850 log service)
* *

@ -121,44 +121,6 @@ typedef enum {
IEC61850_CURRENCY = 30, IEC61850_CURRENCY = 30,
IEC61850_OPTFLDS = 31, /* bit-string(10) */ IEC61850_OPTFLDS = 31, /* bit-string(10) */
IEC61850_TRGOPS = 32 /* bit-string(6) */ 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; } DataAttributeType;
typedef enum { typedef enum {

@ -3,7 +3,7 @@
* *
* IEC 61850 server API for libiec61850. * IEC 61850 server API for libiec61850.
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -42,6 +42,13 @@ extern "C" {
#include "iso_connection_parameters.h" #include "iso_connection_parameters.h"
#include "iec61850_config_file_parser.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 * \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) */ /** integrity report start times will by synchronized with straight numbers (default: false) */
bool syncIntegrityReportTimes; 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 LIB61850_API bool
IedServerConfig_isLogServiceEnabled(IedServerConfig self); 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 * An opaque handle for an IED server instance
*/ */
@ -687,6 +718,21 @@ IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcb
LIB61850_API void LIB61850_API void
IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag); 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 LIB61850_API void
IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter); IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter);
/** /**
* \brief get the peer address of this connection as string * \brief get the peer address of this connection as string
* *
@ -770,6 +814,14 @@ typedef void (*IedConnectionIndicationHandler) (IedServer self, ClientConnection
LIB61850_API void LIB61850_API void
IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter); 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 LIB61850_API int
ControlAction_getCtlNum(ControlAction self); 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 * \brief Check if the control callback is called by a select or operate command
* *
@ -1820,6 +1892,121 @@ typedef MmsDataAccessError
LIB61850_API void LIB61850_API void
IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); 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);
/**@}*/ /**@}*/
/**@}*/ /**@}*/

@ -3,7 +3,7 @@
* *
* Library private function definitions for IedServer. * Library private function definitions for IedServer.
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -50,6 +50,7 @@ struct sIedServer
bool enableBRCBResvTms; bool enableBRCBResvTms;
bool enableOwnerForRCB; bool enableOwnerForRCB;
bool syncIntegrityReportTimes; bool syncIntegrityReportTimes;
uint8_t rcbSettingsWritable;
#endif #endif
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
@ -78,6 +79,8 @@ struct sIedServer
uint8_t edition; uint8_t edition;
uint8_t timeQuality; /* user settable time quality for internally updated times */
bool running; bool running;
}; };

@ -121,7 +121,7 @@ LIB61850_INTERNAL void
Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs); Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs);
LIB61850_INTERNAL MmsValue* 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 LIB61850_INTERNAL MmsDataAccessError
LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig,

@ -325,7 +325,10 @@ struct sMmsMapping {
/* flag indicates if data model is locked --> prevents reports to be sent */ /* flag indicates if data model is locked --> prevents reports to be sent */
bool isModelLocked; bool isModelLocked;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore isModelLockedMutex; Semaphore isModelLockedMutex;
#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */
IedServer iedServer; IedServer iedServer;
@ -334,6 +337,21 @@ struct sMmsMapping {
IedServer_RCBEventHandler rcbEventHandler; IedServer_RCBEventHandler rcbEventHandler;
void* rcbEventHandlerParameter; 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_ */ #endif /* MMS_MAPPING_INTERNAL_H_ */

@ -140,7 +140,7 @@ LIB61850_INTERNAL MmsDataAccessError
Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value,
MmsServerConnection connection); MmsServerConnection connection);
LIB61850_INTERNAL void LIB61850_INTERNAL bool
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName); ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName);
LIB61850_INTERNAL void LIB61850_INTERNAL void

@ -1,7 +1,7 @@
/* /*
* ied_server.c * ied_server.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -580,12 +580,19 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB; self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB;
self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB; self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB;
self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes; self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes;
self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable;
} }
else { else {
self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE;
self->enableOwnerForRCB = false; self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = 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) #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1)
self->enableBRCBResvTms = true; self->enableBRCBResvTms = true;
#else #else
@ -659,6 +666,7 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio
} }
#endif #endif
IedServer_setTimeQuality(self, true, false, false, 10);
} }
else { else {
IedServer_destroy(self); IedServer_destroy(self);
@ -688,6 +696,27 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler,
self->mmsMapping->rcbEventHandlerParameter = parameter; 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 void
IedServer_destroy(IedServer self) IedServer_destroy(IedServer self)
{ {
@ -910,11 +939,15 @@ IedServer_lockDataModel(IedServer self)
{ {
MmsServer_lockModel(self->mmsServer); MmsServer_lockModel(self->mmsServer);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex); Semaphore_wait(self->mmsMapping->isModelLockedMutex);
#endif
self->mmsMapping->isModelLocked = true; self->mmsMapping->isModelLocked = true;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex); Semaphore_post(self->mmsMapping->isModelLockedMutex);
#endif
} }
void void
@ -928,13 +961,17 @@ IedServer_unlockDataModel(IedServer self)
/* check if reports have to be sent! */ /* check if reports have to be sent! */
Reporting_processReportEventsAfterUnlock(self->mmsMapping); Reporting_processReportEventsAfterUnlock(self->mmsMapping);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->mmsMapping->isModelLockedMutex); Semaphore_wait(self->mmsMapping->isModelLockedMutex);
#endif
MmsServer_unlockModel(self->mmsServer); MmsServer_unlockModel(self->mmsServer);
self->mmsMapping->isModelLocked = false; self->mmsMapping->isModelLocked = false;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->mmsMapping->isModelLockedMutex); Semaphore_post(self->mmsMapping->isModelLockedMutex);
#endif
} }
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) #if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
@ -1479,7 +1516,7 @@ IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribu
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->dataModelLock); Semaphore_wait(self->dataModelLock);
#endif #endif
MmsValue_setUtcTimeMs(dataAttribute->mmsValue, value); MmsValue_setUtcTimeMsEx(dataAttribute->mmsValue, value, self->timeQuality);
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->dataModelLock); Semaphore_post(self->dataModelLock);
#endif #endif
@ -1882,7 +1919,6 @@ private_IedServer_removeClientConnection(IedServer self, ClientConnection client
#endif #endif
} }
void void
IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId)
{ {
@ -1890,3 +1926,44 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId)
self->mmsMapping->gooseInterfaceId = StringUtils_copyString(interfaceId); self->mmsMapping->gooseInterfaceId = StringUtils_copyString(interfaceId);
#endif #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;
}

@ -1,7 +1,7 @@
/* /*
* ied_server_config.c * ied_server_config.c
* *
* Copyright 2018-2022 Michael Zillgith * Copyright 2018-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -59,6 +59,12 @@ IedServerConfig_create()
self->enableResvTmsForBRCB = true; self->enableResvTmsForBRCB = true;
self->enableOwnerForRCB = false; self->enableOwnerForRCB = false;
self->syncIntegrityReportTimes = 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; return self;
@ -264,3 +270,34 @@ IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self)
{ {
return self->syncIntegrityReportTimes; 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);
}

@ -258,7 +258,7 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject
MmsValue_update(trkInst->ctlNum->mmsValue, controlObject->ctlNum); MmsValue_update(trkInst->ctlNum->mmsValue, controlObject->ctlNum);
if (trkInst->operTm) if (trkInst->operTm)
MmsValue_setUtcTimeMs(trkInst->operTm->mmsValue, controlObject->operateTime); MmsValue_setUtcTimeMsEx(trkInst->operTm->mmsValue, controlObject->operateTime, self->iedServer->timeQuality);
if (trkInst->respAddCause) if (trkInst->respAddCause)
MmsValue_update(trkInst->respAddCause->mmsValue, controlObject->addCause); MmsValue_update(trkInst->respAddCause->mmsValue, controlObject->addCause);
@ -394,7 +394,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t) if (trkInst->t)
MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode) if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue, errVal); MmsValue_setInt32(trkInst->errorCode->mmsValue, errVal);
@ -590,9 +590,7 @@ setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs)
if (self->tOpOk) { if (self->tOpOk) {
MmsValue* timestamp = self->tOpOk->mmsValue; MmsValue* timestamp = self->tOpOk->mmsValue;
MmsValue_setUtcTimeMs(timestamp, currentTimeInMs); MmsValue_setUtcTimeMsEx(timestamp, currentTimeInMs, self->iedServer->timeQuality);
/* TODO update time quality */
} }
self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE; self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE;
@ -862,6 +860,7 @@ executeStateMachine:
MmsValue* operTm = getOperParameterOperTime(controlObject->oper); MmsValue* operTm = getOperParameterOperTime(controlObject->oper);
MmsValue_setUtcTime(operTm, 0); MmsValue_setUtcTime(operTm, 0);
MmsValue_setUtcTimeQuality(operTm, self->iedServer->timeQuality);
} }
else { else {
MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId,
@ -1660,8 +1659,6 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self)
MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, &_varSpecList, &_values, false); MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, &_varSpecList, &_values, false);
} /* ControlObject_sendCommandTerminationNegative() */ } /* ControlObject_sendCommandTerminationNegative() */
void void
ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error, ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error,
ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode) ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode)
@ -1875,7 +1872,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: select not applicable for control model %u\n", controlObject->ctlModel); 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) { 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; goto free_and_return;
} }
@ -2035,7 +2032,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
MmsValue* test = getOperParameterTest(value); MmsValue* test = getOperParameterTest(value);
if (checkValidityOfOriginParameter(origin) == false) { 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, ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR,
ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true); ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true);
@ -2142,7 +2139,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
} }
} }
else { else {
indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
} }
} }
else { else {
@ -2163,12 +2160,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL) if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL)
|| (timeParameter == NULL)) { || (timeParameter == NULL)) {
indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
goto free_and_return; goto free_and_return;
} }
if (checkValidityOfOriginParameter(origin) == false) { if (checkValidityOfOriginParameter(origin) == false) {
indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
ControlObject_sendLastApplError(controlObject, connection, "Oper", ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
@ -2224,7 +2221,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
(controlObject->testMode == testCondition) (controlObject->testMode == testCondition)
) == false) ) == false)
{ {
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
ControlObject_sendLastApplError(controlObject, connection, "Oper", ControlObject_sendLastApplError(controlObject, connection, "Oper",
CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS,
@ -2372,7 +2369,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari
MmsValue* origin = getCancelParameterOrigin(value); MmsValue* origin = getCancelParameterOrigin(value);
if ((ctlNum == NULL) || (origin == NULL)) { if ((ctlNum == NULL) || (origin == NULL)) {
indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED;
if (DEBUG_IED_SERVER) if (DEBUG_IED_SERVER)
printf("IED_SERVER: Invalid cancel message!\n"); printf("IED_SERVER: Invalid cancel message!\n");
goto free_and_return; goto free_and_return;
@ -2501,6 +2498,22 @@ ControlAction_getCtlNum(ControlAction self)
return -1; 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 bool
ControlAction_isSelect(ControlAction self) ControlAction_isSelect(ControlAction self)
{ {

@ -1,7 +1,7 @@
/* /*
* logging.c * logging.c
* *
* Copyright 2016-2022 Michael Zillgith * Copyright 2016-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -43,6 +43,8 @@
#if (CONFIG_IEC61850_LOG_SERVICE == 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1)
static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}};
LogInstance* LogInstance*
LogInstance_create(LogicalNode* parentLN, const char* name) LogInstance_create(LogicalNode* parentLN, const char* name)
{ {
@ -418,7 +420,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, LogControl* logControl, IEC6
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t) if (trkInst->t)
MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode) if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue, MmsValue_setInt32(trkInst->errorCode->mmsValue,
@ -520,6 +522,19 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma
if (logControl == NULL) { if (logControl == NULL) {
return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; 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) { if (strcmp(varName, "LogEna") == 0) {
bool logEna = MmsValue_getBoolean(value); bool logEna = MmsValue_getBoolean(value);
@ -699,7 +714,7 @@ exit_function:
} }
MmsValue* 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; MmsValue* value = NULL;
@ -723,27 +738,41 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain,
char* varName = MmsMapping_getNextNameElement(objectName); char* varName = MmsMapping_getNextNameElement(objectName);
if (varName != NULL) if (varName)
*(varName - 1) = 0; *(varName - 1) = 0;
LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); 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 = &objectAccessDenied;
value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); }
} }
else {
value = logControl->mmsValue; if (allowAccess) {
updateLogStatusInLCB(logControl);
if (varName) {
value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName);
}
else {
value = logControl->mmsValue;
}
} }
} }
return value; return value;
} }
static char* static char*
createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl) createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl)
{ {

@ -1,7 +1,7 @@
/* /*
* mms_goose.c * mms_goose.c
* *
* Copyright 2013-2021 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -231,7 +231,7 @@ updateGenericTrackingObjectValues(MmsGooseControlBlock gc, IEC61850_ServiceType
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t) if (trkInst->t)
MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), gc->mmsMapping->iedServer->timeQuality);
if (trkInst->errorCode) if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue, MmsValue_setInt32(trkInst->errorCode->mmsValue,

@ -1,7 +1,7 @@
/* /*
* mms_mapping.c * mms_mapping.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -25,6 +25,7 @@
#include "mms_mapping.h" #include "mms_mapping.h"
#include "mms_mapping_internal.h" #include "mms_mapping_internal.h"
#include "mms_server_internal.h" #include "mms_server_internal.h"
#include "mms_value_internal.h"
#include "stack_config.h" #include "stack_config.h"
#include "mms_goose.h" #include "mms_goose.h"
@ -68,6 +69,8 @@ typedef struct
uint64_t reservationTimeout; uint64_t reservationTimeout;
} SettingGroup; } SettingGroup;
static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}};
#if (CONFIG_IEC61850_CONTROL_SERVICE == 1) #if (CONFIG_IEC61850_CONTROL_SERVICE == 1)
bool bool
@ -136,7 +139,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
sizeof(MmsVariableSpecification)); sizeof(MmsVariableSpecification));
namedVariable = namedVariable->typeSpec.array.elementTypeSpec; namedVariable = namedVariable->typeSpec.array.elementTypeSpec;
if (attribute->firstChild && ((DataAttribute*)(attribute->firstChild))->type != IEC61850_CONSTRUCTED) { if (attribute->type != IEC61850_CONSTRUCTED) {
isBasicArray = true; isBasicArray = true;
} }
} }
@ -157,7 +160,8 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute)
DataAttribute* subDataAttribute = (DataAttribute*) attribute->firstChild; DataAttribute* subDataAttribute = (DataAttribute*) attribute->firstChild;
int i = 0; int i = 0;
while (subDataAttribute != NULL) { while (subDataAttribute)
{
namedVariable->typeSpec.structure.elements[i] = namedVariable->typeSpec.structure.elements[i] =
createNamedVariableFromDataAttribute(subDataAttribute); createNamedVariableFromDataAttribute(subDataAttribute);
@ -693,7 +697,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, SettingGroupControlBlock* sg
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t) if (trkInst->t)
MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode) if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue, MmsValue_setInt32(trkInst->errorCode->mmsValue,
@ -840,7 +844,7 @@ MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock*
MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4);
MmsValue_setUint8(actSg, sgcb->actSG); MmsValue_setUint8(actSg, sgcb->actSG);
MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality);
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
copySGCBValuesToTrackingObject(self, sgcb); copySGCBValuesToTrackingObject(self, sgcb);
@ -2720,7 +2724,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain,
MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4);
MmsValue_setUint8(actSg, sg->sgcb->actSG); MmsValue_setUint8(actSg, sg->sgcb->actSG);
MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality);
} }
else else
retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; 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) #if (CONFIG_IEC61850_LOG_SERVICE == 1)
/* LOG control block - LG */ /* LOG control block - LG */
if (isLogControlBlock(separator)) { if (isLogControlBlock(separator)) {
retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId); retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId, connection);
goto exit_function; goto exit_function;
} }
#endif #endif
@ -3155,31 +3159,35 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo
continue; continue;
if (strlen(rc->name) == variableIdLen) { if (strlen(rc->name) == variableIdLen) {
if (strncmp(variableId, rc->name, variableIdLen) == 0) { if (strncmp(variableId, rc->name, variableIdLen) == 0)
{
char* elementName = MmsMapping_getNextNameElement(reportName); char* elementName = MmsMapping_getNextNameElement(reportName);
ReportControl_readAccess(rc, self, connection, elementName);
MmsValue* value = NULL; MmsValue* value = NULL;
if (ReportControl_readAccess(rc, self, connection, elementName))
{
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
if (elementName != NULL) if (elementName != NULL)
value = ReportControl_getRCBValue(rc, elementName); value = ReportControl_getRCBValue(rc, elementName);
else else
value = rc->rcbValues; value = rc->rcbValues;
if (value) { if (value) {
value = MmsValue_clone(value); value = MmsValue_clone(value);
MmsValue_setDeletableRecursive(value); MmsValue_setDeletableRecursive(value);
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(rc->rcbValuesLock); Semaphore_post(rc->rcbValuesLock);
#endif #endif
}
else {
value = &objectAccessDenied;
}
retValue = value; retValue = value;
@ -3221,6 +3229,46 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection)
} }
#endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ #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 */ static void /* is called by MMS server layer and runs in the connection handling thread */
mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event) mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event)
{ {
@ -3317,18 +3365,16 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS
{ {
return DATA_ACCESS_ERROR_SUCCESS; return DATA_ACCESS_ERROR_SUCCESS;
} }
else { else
{
StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId);
LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str);
if (ln != NULL) { if (ln != NULL)
{
char* doStart = strchr(separator + 1, '$'); char* doStart = strchr(separator + 1, '$');
if (doStart != NULL) { if (doStart != NULL) {
char* doEnd = strchr(doStart + 1, '$'); 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; 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 static MmsError
variableListChangedHandler (void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain, variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection) char* listName, MmsServerConnection connection)
{ {
MmsError allow = MMS_ERROR_NONE; MmsError allow = MMS_ERROR_NONE;
(void)connection; MmsMapping* self = (MmsMapping*) parameter;
/* TODO add log message */ /* TODO add log message */
#if (DEBUG_IED_SERVER == 1) #if (DEBUG_IED_SERVER == 1)
if (create) if (accessType == MMS_VARLIST_CREATE)
printf("IED_SERVER: create data set "); printf("IED_SERVER: create data set ");
else else if (accessType == MMS_VARLIST_DELETE)
printf("IED_SERVER: delete data set "); 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) { switch (listType) {
case MMS_VMD_SPECIFIC: case MMS_VMD_SPECIFIC:
@ -3404,120 +3494,172 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li
printf("specific (name=%s)\n", listName); printf("specific (name=%s)\n", listName);
#endif /* (DEBUG_IED_SERVER == 1) */ #endif /* (DEBUG_IED_SERVER == 1) */
MmsMapping* self = (MmsMapping*) parameter; if (accessType == MMS_VARLIST_CREATE) {
if (create) { if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_CREATE)) {
if (listType == MMS_DOMAIN_SPECIFIC) {
/* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */
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) { char* separator = strchr(listName, '$');
int lnNameLen = separator - listName;
memcpy(lnName, listName, lnNameLen); if (separator != NULL) {
lnName[lnNameLen] = 0; int lnNameLen = separator - listName;
if (LogicalDevice_getLogicalNode(ld, lnName) != NULL) memcpy(lnName, listName, lnNameLen);
allow = MMS_ERROR_NONE; lnName[lnNameLen] = 0;
}
} if (LogicalDevice_getLogicalNode(ld, lnName) != NULL)
allow = MMS_ERROR_NONE;
}
}
}
}
else {
allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED;
} }
} }
else { else if (accessType == MMS_VARLIST_DELETE) {
/* Check if data set is referenced in a report */
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) { LinkedList rcElement = self->reportControls;
ReportControl* rc = (ReportControl*) rcElement->data;
if (rc->isDynamicDataSet) { while ((rcElement = LinkedList_getNext(rcElement)) != NULL) {
if (rc->dataSet != NULL) { ReportControl* rc = (ReportControl*) rcElement->data;
if (listType == MMS_DOMAIN_SPECIFIC) { if (rc->isDynamicDataSet) {
if (rc->dataSet->logicalDeviceName != NULL) { if (rc->dataSet != NULL) {
if (strcmp(rc->dataSet->name, listName) == 0) {
if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { if (listType == MMS_DOMAIN_SPECIFIC) {
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; if (rc->dataSet->logicalDeviceName != NULL) {
break; 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) {
else if (listType == MMS_VMD_SPECIFIC) { if (rc->dataSet->logicalDeviceName == NULL) {
if (rc->dataSet->logicalDeviceName == NULL) { if (strcmp(rc->dataSet->name, listName) == 0) {
if (strcmp(rc->dataSet->name, listName) == 0) { allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; break;
break; }
} }
} }
} else if (listType == MMS_ASSOCIATION_SPECIFIC) {
else if (listType == MMS_ASSOCIATION_SPECIFIC) { if (rc->dataSet->logicalDeviceName == NULL) {
if (rc->dataSet->logicalDeviceName == NULL) { if (strcmp(rc->dataSet->name, listName) == 0) {
if (strcmp(rc->dataSet->name, listName) == 0) { allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; break;
break; }
} }
} }
}
}
} }
} }
}
#if (CONFIG_IEC61850_LOG_SERVICE == 1) #if (CONFIG_IEC61850_LOG_SERVICE == 1)
/* check if data set is referenced in a log control block*/ /* check if data set is referenced in a log control block*/
LinkedList logElement = self->logControls; LinkedList logElement = self->logControls;
while ((logElement = LinkedList_getNext(logElement)) != NULL) { while ((logElement = LinkedList_getNext(logElement)) != NULL) {
LogControl* lc = (LogControl*) logElement->data; LogControl* lc = (LogControl*) logElement->data;
if (lc->isDynamicDataSet) { if (lc->isDynamicDataSet) {
if (lc->dataSet != NULL) { if (lc->dataSet != NULL) {
if (listType == MMS_DOMAIN_SPECIFIC) { if (listType == MMS_DOMAIN_SPECIFIC) {
if (lc->dataSet->logicalDeviceName != NULL) { if (lc->dataSet->logicalDeviceName != NULL) {
if (strcmp(lc->dataSet->name, listName) == 0) { if (strcmp(lc->dataSet->name, listName) == 0) {
if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) {
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
break; break;
}
} }
} }
} }
} else if (listType == MMS_VMD_SPECIFIC) {
else if (listType == MMS_VMD_SPECIFIC) { if (lc->dataSet->logicalDeviceName == NULL) {
if (lc->dataSet->logicalDeviceName == NULL) { if (strcmp(lc->dataSet->name, listName) == 0) {
if (strcmp(lc->dataSet->name, listName) == 0) { allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT;
allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; break;
break; }
} }
} }
}
}
} }
} }
}
#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ #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; 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 void
MmsMapping_installHandlers(MmsMapping* self) MmsMapping_installHandlers(MmsMapping* self)
{ {
@ -3525,7 +3667,12 @@ MmsMapping_installHandlers(MmsMapping* self)
MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self);
MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self);
MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (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 void
@ -3716,7 +3863,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
{ {
LinkedList element = self->reportControls; LinkedList element = self->reportControls;
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex); Semaphore_wait(self->isModelLockedMutex);
#endif
bool modelLocked = self->isModelLocked; bool modelLocked = self->isModelLocked;
@ -3732,8 +3881,7 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
continue; continue;
break; break;
case REPORT_CONTROL_VALUE_CHANGED: case REPORT_CONTROL_VALUE_CHANGED:
if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) && if ((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0)
((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0))
continue; continue;
break; break;
case REPORT_CONTROL_QUALITY_CHANGED: case REPORT_CONTROL_QUALITY_CHANGED:
@ -3754,7 +3902,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag)
Reporting_processReportEventsAfterUnlock(self); Reporting_processReportEventsAfterUnlock(self);
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex); Semaphore_post(self->isModelLockedMutex);
#endif
} }
#endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */
@ -3775,13 +3925,17 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value)
if (DataSet_isMemberValue(dataSet, value, NULL)) { if (DataSet_isMemberValue(dataSet, value, NULL)) {
MmsGooseControlBlock_setStateChangePending(gcb); MmsGooseControlBlock_setStateChangePending(gcb);
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex); Semaphore_wait(self->isModelLockedMutex);
#endif
if (self->isModelLocked == false) { if (self->isModelLocked == false) {
MmsGooseControlBlock_publishNewState(gcb); MmsGooseControlBlock_publishNewState(gcb);
} }
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_post(self->isModelLockedMutex); Semaphore_post(self->isModelLockedMutex);
#endif
} }
} }
} }

@ -1,7 +1,7 @@
/* /*
* reporting.c * reporting.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -579,7 +579,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_
MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType);
if (trkInst->t) if (trkInst->t)
MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality);
if (trkInst->errorCode) if (trkInst->errorCode)
MmsValue_setInt32(trkInst->errorCode->mmsValue, 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 static bool
updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection) updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection)
{ {
@ -680,6 +717,8 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
MmsValue* dataSetValue; MmsValue* dataSetValue;
bool isUsedDataSetDynamic = rc->isDynamicDataSet;
if (newDatSet != NULL) { if (newDatSet != NULL) {
if (strcmp(MmsValue_toString(newDatSet), "") == 0) { if (strcmp(MmsValue_toString(newDatSet), "") == 0) {
success = true; success = true;
@ -751,20 +790,32 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
} }
} }
if (rc->isDynamicDataSet) { if (dataSetValue) {
if (rc->dataSet && dataSetChanged) {
deleteDataSetValuesShadowBuffer(rc);
MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet);
rc->isDynamicDataSet = false;
rc->dataSet = NULL;
}
}
if (dataSetValue && dataSetChanged) {
const char* dataSetName = MmsValue_toString(dataSetValue); const char* dataSetName = MmsValue_toString(dataSetValue);
DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName); 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 (MMS_DYNAMIC_DATA_SETS == 1)
if (dataSet == NULL) { if (dataSet == NULL) {
dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName); dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName);
@ -779,19 +830,28 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
MmsNamedVariableList mmsVariableList MmsNamedVariableList mmsVariableList
= MmsServerConnection_getNamedVariableList(connection, dataSetName + 1); = MmsServerConnection_getNamedVariableList(connection, dataSetName + 1);
if (mmsVariableList != NULL) if (mmsVariableList) {
if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) {
goto exit_function;
}
dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList);
}
} }
} }
} }
/* check for VMD specific data set */ /* check for VMD specific data set */
else if (dataSetName[0] == '/') { else if (dataSetName[0] == '/') {
MmsNamedVariableList mmsVariableList = MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1); 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); dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList);
}
} }
} }
@ -810,11 +870,20 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
#endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ #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 */ /* delete pending event and create buffer for new data set */
deleteDataSetValuesShadowBuffer(rc); deleteDataSetValuesShadowBuffer(rc);
if (isUsedDataSetDynamic) {
if (rc->dataSet) {
MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet);
}
}
rc->dataSet = dataSet; rc->dataSet = dataSet;
createDataSetValuesShadowBuffer(rc); createDataSetValuesShadowBuffer(rc);
@ -830,7 +899,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet,
GLOBAL_FREEMEM(rc->inclusionFlags); GLOBAL_FREEMEM(rc->inclusionFlags);
rc->inclusionFlags = (uint8_t*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t)); rc->inclusionFlags = (uint8_t*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t));
} }
success = true; success = true;
@ -862,7 +930,6 @@ createDataSetReferenceForDefaultDataSet(ReportControlBlock* rcb, ReportControl*
return dataSetReference; return dataSetReference;
} }
static MmsValue* static MmsValue*
createOptFlds(ReportControlBlock* reportControlBlock) createOptFlds(ReportControlBlock* reportControlBlock)
{ {
@ -1697,21 +1764,35 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc)
} }
} }
void bool
ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName) ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName)
{ {
(void)elementName; bool accessAllowed = true;
MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS;
/* check reservation timeout */ /* check reservation timeout */
if (rc->buffered) { if (rc->buffered) {
checkReservationTimeout(mmsMapping, rc); checkReservationTimeout(mmsMapping, rc);
} }
if (mmsMapping->rcbEventHandler) { ClientConnection clientConnection = NULL;
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection);
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 static bool
@ -1809,6 +1890,15 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); 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 */ /* check reservation timeout for buffered RCBs */
if (rc->buffered) { if (rc->buffered) {
@ -1923,6 +2013,9 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
if (updateReportDataset(self, rc, NULL, connection)) { if (updateReportDataset(self, rc, NULL, connection)) {
if (rc->reserved == false) { if (rc->reserved == false) {
rc->resvTms = RESV_TMS_IMPLICIT_VALUE;
reserveRcb(rc, connection); reserveRcb(rc, connection);
if (self->rcbEventHandler) { if (self->rcbEventHandler) {
@ -2102,6 +2195,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
} }
else if (strcmp(elementName, "DatSet") == 0) { 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) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
@ -2149,6 +2248,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
} }
else if (strcmp(elementName, "IntgPd") == 0) { 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) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
@ -2196,6 +2301,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
} }
else if (strcmp(elementName, "TrgOps") == 0) { 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) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
@ -2269,6 +2380,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
else if (strcmp(elementName, "BufTm") == 0) { 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) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
@ -2302,6 +2419,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
} }
else if (strcmp(elementName, "RptID") == 0) { 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) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
#endif #endif
@ -2409,6 +2532,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme
goto exit_function; 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) #if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(rc->rcbValuesLock); Semaphore_wait(rc->rcbValuesLock);
@ -2459,7 +2590,6 @@ exit_function:
} }
} }
} }
else if (rc->resvTms == -1) { else if (rc->resvTms == -1) {
if (rc->reserved == false) { if (rc->reserved == false) {
@ -2491,6 +2621,8 @@ exit_function:
ReportControl_unlockNotify(rc); ReportControl_unlockNotify(rc);
exit_function_only_tracking:
#if (CONFIG_IEC61850_SERVICE_TRACKING == 1) #if (CONFIG_IEC61850_SERVICE_TRACKING == 1)
if (rc->buffered) if (rc->buffered)
updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal); updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal);
@ -3339,6 +3471,8 @@ sendNextReportEntrySegment(ReportControl* self)
MmsValue* subSeqNum = self->subSeqVal; MmsValue* subSeqNum = self->subSeqVal;
int numberOfAddedElements = 0;
for (i = 0; i < self->dataSet->elementCount; i++) { for (i = 0; i < self->dataSet->elementCount; i++) {
if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) { if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) {
@ -3420,6 +3554,8 @@ sendNextReportEntrySegment(ReportControl* self)
MmsValue_setBitStringBit(self->inclusionField, i, true); MmsValue_setBitStringBit(self->inclusionField, i, true);
numberOfAddedElements++;
accessResultSize += elementSize; accessResultSize += elementSize;
estimatedSegmentSize += elementSize; estimatedSegmentSize += elementSize;
} }
@ -3460,11 +3596,16 @@ sendNextReportEntrySegment(ReportControl* self)
uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize);
uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize);
if ((int) completeMessageSize > maxMmsPduSize) { if (((int) completeMessageSize > maxMmsPduSize) || (numberOfAddedElements == 0)) {
if (DEBUG_IED_SERVER) 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 */ /* encode the report message */
@ -3688,6 +3829,8 @@ sendNextReportEntrySegment(ReportControl* self)
self->startIndexForNextSegment = maxIndex; self->startIndexForNextSegment = maxIndex;
} }
exit_remove_report:
if (segmented == false) { if (segmented == false) {
assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next); assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next);
@ -3880,7 +4023,9 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs)
void void
Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs)
{ {
#if (CONFIG_MMS_THREADLESS_STACK != 1)
Semaphore_wait(self->isModelLockedMutex); Semaphore_wait(self->isModelLockedMutex);
#endif
if (self->isModelLocked == false) { 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); Semaphore_post(self->isModelLockedMutex);
#endif
} }
/* /*

@ -676,7 +676,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot
CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat); CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat);
addCommonOperateElements(cancel, isTimeActivated, true); addCommonOperateElements(cancel, isTimeActivated, false);
} }
} }

@ -347,6 +347,18 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe
return self; return self;
} }
const char*
LogControlBlock_getName(LogControlBlock* self)
{
return self->name;
}
LogicalNode*
LogControlBlock_getParent(LogControlBlock* self)
{
return self->parent;
}
static void static void
LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb) LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb)
{ {
@ -676,7 +688,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
self->triggerOptions = triggerOptions; self->triggerOptions = triggerOptions;
self->sAddr = sAddr; self->sAddr = sAddr;
if ((arrayElements > 0) && (type != IEC61850_CONSTRUCTED)) { if (arrayElements > 0) {
int i; int i;
for (i = 0; i < arrayElements; i++) { for (i = 0; i < arrayElements; i++) {
@ -691,7 +703,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type
arrayElement->fc = fc; arrayElement->fc = fc;
arrayElement->firstChild = NULL; arrayElement->firstChild = NULL;
arrayElement->mmsValue = NULL; arrayElement->mmsValue = NULL;
arrayElement->parent = parent; arrayElement->parent = (ModelNode*)self;
arrayElement->sibling = NULL; arrayElement->sibling = NULL;
arrayElement->triggerOptions = triggerOptions; arrayElement->triggerOptions = triggerOptions;
arrayElement->sAddr = sAddr; arrayElement->sAddr = sAddr;

@ -649,106 +649,110 @@ ModelNode_getChildCount(ModelNode* modelNode) {
ModelNode* ModelNode*
ModelNode_getChild(ModelNode* self, const char* name) ModelNode_getChild(ModelNode* self, const char* name)
{ {
/* check for element separator */ /* check for element separator */
const char* separator = strchr(name, '.'); const char* separator = strchr(name, '.');
/* allow first character to be "." */ /* allow first character to be "." */
if (separator == name) if (separator == name)
name++; name++;
/* check for array separator */ /* check for array separator */
const char* arraySeparator = strchr(name, '('); const char* arraySeparator = strchr(name, '(');
if (arraySeparator) { if (arraySeparator) {
const char* arraySeparator2 = strchr(arraySeparator, ')'); const char* arraySeparator2 = strchr(arraySeparator, ')');
if (arraySeparator2) { if (arraySeparator2) {
int idx = (int) strtol(arraySeparator + 1, NULL, 10); int idx = (int) strtol(arraySeparator + 1, NULL, 10);
ModelNode* arrayNode = NULL; ModelNode* arrayNode = NULL;
if (name == arraySeparator) { if (name == arraySeparator) {
arrayNode = ModelNode_getChildWithIdx(self, idx); arrayNode = ModelNode_getChildWithIdx(self, idx);
} }
else { else {
char nameCopy[65]; char nameCopy[65];
const char* pos = name; const char* pos = name;
int cpyIdx = 0; int cpyIdx = 0;
while (pos < arraySeparator) { while (pos < arraySeparator) {
nameCopy[cpyIdx] = *pos; nameCopy[cpyIdx] = *pos;
cpyIdx++; cpyIdx++;
pos++; pos++;
} }
nameCopy[cpyIdx] = 0; nameCopy[cpyIdx] = 0;
ModelNode* childNode = ModelNode_getChild(self, nameCopy); ModelNode* childNode = ModelNode_getChild(self, nameCopy);
if (childNode) { if (childNode) {
arrayNode = ModelNode_getChildWithIdx(childNode, idx); arrayNode = ModelNode_getChildWithIdx(childNode, idx);
} }
else else
return NULL; return NULL;
} }
if (arrayNode) { if (arrayNode) {
if (*(arraySeparator2 + 1) == 0) { if (*(arraySeparator2 + 1) == 0) {
return arrayNode; return arrayNode;
} }
else { else {
if (*(arraySeparator2 + 1) == '.') if (*(arraySeparator2 + 1) == '.')
return ModelNode_getChild(arrayNode, arraySeparator2 + 2); return ModelNode_getChild(arrayNode, arraySeparator2 + 2);
else else
return ModelNode_getChild(arrayNode, arraySeparator2 + 1); return ModelNode_getChild(arrayNode, arraySeparator2 + 1);
} }
} }
else else
return NULL; 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) ModelNode* nextNode = self->firstChild;
nameElementLength = (separator - name);
else
nameElementLength = strlen(name);
ModelNode* nextNode = self->firstChild; ModelNode* matchingNode = NULL;
ModelNode* matchingNode = NULL; while (nextNode) {
while (nextNode) { if (nextNode->name == NULL) {
int nodeNameLen = strlen(nextNode->name); break; /* is an array element */
}
if (nodeNameLen == nameElementLength) { int nodeNameLen = strlen(nextNode->name);
if (memcmp(nextNode->name, name, nodeNameLen) == 0) { if (nodeNameLen == nameElementLength) {
matchingNode = nextNode;
break;
}
}
nextNode = nextNode->sibling; if (memcmp(nextNode->name, name, nodeNameLen) == 0) {
} matchingNode = nextNode;
break;
}
}
if ((separator != NULL) && (matchingNode != NULL)) { nextNode = nextNode->sibling;
return ModelNode_getChild(matchingNode, separator + 1); }
}
else if ((separator != NULL) && (matchingNode != NULL)) {
return matchingNode; return ModelNode_getChild(matchingNode, separator + 1);
}
else
return matchingNode;
} }
ModelNode* ModelNode*

@ -1,24 +1,24 @@
/* /*
* asn1_ber_primitive_value.c * 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 * libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* libIEC61850 is distributed in the hope that it will be useful, * libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>. * along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
* *
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"

@ -1,7 +1,7 @@
/* /*
* ber_decoder.c * ber_decoder.c
* *
* Copyright 2013 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *

@ -1,7 +1,7 @@
/* /*
* ber_encoder.c * ber_encoder.c
* *
* Copyright 2013 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -356,6 +356,28 @@ BerEncoder_UInt32determineEncodedSize(uint32_t value)
return size; 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 int
BerEncoder_determineLengthSize(uint32_t length) BerEncoder_determineLengthSize(uint32_t length)
{ {
@ -457,7 +479,6 @@ BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufL
requiredBytes--; requiredBytes--;
} }
} }
} }
return encodedBytes; return encodedBytes;

@ -1,7 +1,7 @@
/* /*
* ber_integer.c * ber_integer.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2022 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *

@ -1,7 +1,7 @@
/* /*
* iso_connection_parameters.h * iso_connection_parameters.h
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -24,6 +24,10 @@
#ifndef ISO_CONNECTION_PARAMETERS_H_ #ifndef ISO_CONNECTION_PARAMETERS_H_
#define ISO_CONNECTION_PARAMETERS_H_ #define ISO_CONNECTION_PARAMETERS_H_
#ifndef CONFIG_MMS_SUPPORT_TLS
#define CONFIG_MMS_SUPPORT_TLS 0
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -145,6 +149,9 @@ struct sIsoConnectionParameters
const char* hostname; const char* hostname;
int tcpPort; int tcpPort;
const char* localIpAddress;
int localTcpPort;
uint8_t remoteApTitle[10]; uint8_t remoteApTitle[10];
int remoteApTitleLen; int remoteApTitleLen;
int remoteAEQualifier; int remoteAEQualifier;
@ -215,6 +222,20 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s
LIB61850_API void LIB61850_API void
IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort); 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 * \brief set the remote AP-Title and AE-Qualifier
* *

@ -648,6 +648,23 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms
MmsConnection_WriteVariableHandler handler, void* parameter); 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 * \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, uint32_t arrayIndex, const char* componentId, MmsValue* value,
MmsConnection_WriteVariableHandler handler, void* parameter); 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 * \brief Write a single array element or a sub array to an array type variable
* *

@ -1,7 +1,7 @@
/* /*
* mms_server.h * mms_server.h
* *
* Copyright 2013-2018 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -57,30 +57,70 @@ MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress);
LIB61850_INTERNAL bool LIB61850_INTERNAL bool
MmsServer_isRunning(MmsServer self); 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 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 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 domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!)
* \param listName the name * \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 * \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); 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 self the MmsServer instance to operate on
* \param handler the callback handler function * \param handler the callback handler function
* \param parameter user provided parameter that is passed to the callback handler * \param parameter user provided parameter that is passed to the callback handler
*/ */
LIB61850_INTERNAL void 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 * \brief ObtainFile service callback handler
@ -370,6 +410,9 @@ MmsServerConnection_getLocalAddress(MmsServerConnection self);
LIB61850_INTERNAL void* LIB61850_INTERNAL void*
MmsServerConnection_getSecurityToken(MmsServerConnection self); MmsServerConnection_getSecurityToken(MmsServerConnection self);
LIB61850_INTERNAL void
MmsServer_ignoreClientRequests(MmsServer self, bool enable);;
/**@}*/ /**@}*/
#ifdef __cplusplus #ifdef __cplusplus

@ -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) * 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 self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME.
*
* \param timeQuality the byte representing the time quality * \param timeQuality the byte representing the time quality
*/ */
LIB61850_API void LIB61850_API void
MmsValue_setUtcTimeQuality(MmsValue* self, uint8_t timeQuality); 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 * \brief get the TimeQuality byte of the UtcTime
* *

@ -114,4 +114,7 @@ CotpConnection_getRemoteRef(CotpConnection* self);
LIB61850_INTERNAL int LIB61850_INTERNAL int
CotpConnection_getLocalRef(CotpConnection* self); CotpConnection_getLocalRef(CotpConnection* self);
LIB61850_INTERNAL void
CotpConnection_flushBuffer(CotpConnection* self);
#endif /* COTP_H_ */ #endif /* COTP_H_ */

@ -272,6 +272,11 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
int startIndex, int elementCount, int startIndex, int elementCount,
MmsValue* value, ByteBuffer* writeBuffer); 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 LIB61850_INTERNAL int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component, uint32_t arrayIndex, const char* component,

@ -123,8 +123,14 @@ struct sMmsServer {
MmsConnectionHandler connectionHandler; MmsConnectionHandler connectionHandler;
void* connectionHandlerParameter; void* connectionHandlerParameter;
MmsNamedVariableListChangedHandler variableListChangedHandler; /* TODO this is only required if dynamic data sets are supported! */ MmsNamedVariableListAccessHandler variableListAccessHandler;
void* variableListChangedHandlerParameter; void* variableListAccessHandlerParameter;
MmsReadJournalHandler readJournalHandler;
void* readJournalHandlerParameter;
MmsGetNameListHandler getNameListHandler;
void* getNameListHandlerParameter;
AcseAuthenticator authenticator; AcseAuthenticator authenticator;
void* authenticatorParameter; void* authenticatorParameter;
@ -135,7 +141,8 @@ struct sMmsServer {
Map openConnections; Map openConnections;
Map valueCaches; Map valueCaches;
bool isLocked;
bool blockRequests;
ByteBuffer* transmitBuffer; /* global buffer for encoding reports, delayed responses... */ ByteBuffer* transmitBuffer; /* global buffer for encoding reports, delayed responses... */
#if (CONFIG_MMS_THREADLESS_STACK != 1) #if (CONFIG_MMS_THREADLESS_STACK != 1)
@ -420,7 +427,7 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection,
uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults);
LIB61850_INTERNAL MmsError LIB61850_INTERNAL MmsError
mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain, mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain,
char* listName, MmsServerConnection connection); char* listName, MmsServerConnection connection);
#endif /* MMS_SERVER_INTERNAL_H_ */ #endif /* MMS_SERVER_INTERNAL_H_ */

@ -1,7 +1,7 @@
/* /*
* mms_server_libinternal.h * mms_server_libinternal.h
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -196,7 +196,7 @@ MmsServer_getConnectionCounter(MmsServer self);
LIB61850_INTERNAL void LIB61850_INTERNAL void
MmsServer_stopListeningThreadless(MmsServer self); MmsServer_stopListeningThreadless(MmsServer self);
LIB61850_INTERNAL const char* LIB61850_INTERNAL const char*
MmsServer_getFilesystemBasepath(MmsServer self); MmsServer_getFilesystemBasepath(MmsServer self);
#endif /* MMS_SERVER_LIBINTERNAL_H_ */ #endif /* MMS_SERVER_LIBINTERNAL_H_ */

@ -692,8 +692,13 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
/* set timeout for connect */ /* set timeout for connect */
self->nextReadTimeout = Hal_getTimeInMs() + connectTimeoutInMs; 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) { if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) {
Socket_destroy(self->socket); Socket_destroy(self->socket);
self->socket = NULL; self->socket = NULL;
@ -704,9 +709,9 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim
success = false; success = false;
} }
Semaphore_post(self->tickMutex); Semaphore_post(self->tickMutex);
return success; return success;
} }

@ -104,6 +104,18 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const cha
self->tcpPort = tcpPort; 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 void
IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier) IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier)
{ {

@ -5,7 +5,7 @@
* *
* Partial implementation of the ISO 8073 COTP (ISO TP0) protocol for MMS. * 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. * This file is part of libIEC61850.
* *
@ -174,6 +174,38 @@ writeToSocket(CotpConnection* self, uint8_t* buf, int size)
#endif #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 static bool
sendBuffer(CotpConnection* self) sendBuffer(CotpConnection* self)
{ {
@ -182,7 +214,15 @@ sendBuffer(CotpConnection* self)
bool retVal = false; 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) if (sentBytes == -1)
goto exit_function; goto exit_function;
@ -215,33 +255,6 @@ exit_function:
return retVal; 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 CotpIndication
CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) 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; int totalSize = (fragments * (COTP_DATA_HEADER_SIZE + 4)) + payload->length;
/* try to flush extension buffer */ /* try to flush extension buffer */
flushBuffer(self); if (flushBuffer(self) == false) {
return COTP_ERROR;
}
/* check if totalSize will fit in extension buffer */ /* check if totalSize will fit in extension buffer */
if (self->socketExtensionBuffer) { if (self->socketExtensionBuffer) {
@ -281,7 +296,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
int currentChainIndex = 0; int currentChainIndex = 0;
if (DEBUG_COTP) 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; uint8_t* buffer = self->writeBuffer->buffer;
@ -307,7 +322,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload)
if (currentChainIndex >= currentChain->partLength) { if (currentChainIndex >= currentChain->partLength) {
currentChain = currentChain->nextPart; currentChain = currentChain->nextPart;
if (DEBUG_COTP) 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; currentChainIndex = 0;
} }
@ -756,6 +771,13 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size)
#endif #endif
} }
void
CotpConnection_flushBuffer(CotpConnection* self)
{
if (self->socketExtensionBufferFill > 0)
flushBuffer(self);
}
TpktState TpktState
CotpConnection_readToTpktBuffer(CotpConnection* self) CotpConnection_readToTpktBuffer(CotpConnection* self)
{ {
@ -765,6 +787,14 @@ CotpConnection_readToTpktBuffer(CotpConnection* self)
assert (bufferSize > 4); assert (bufferSize > 4);
if (self->socketExtensionBufferFill > 0) {
if (flushBuffer(self) == false)
goto exit_error;
if (self->socketExtensionBufferFill > 0)
goto exit_waiting;
}
int readBytes; int readBytes;
if (bufPos < 4) { if (bufPos < 4) {

@ -11,6 +11,8 @@
#include "asn_application.h" /* Application-visible API */ #include "asn_application.h" /* Application-visible API */
#define EMIT_ASN_DEBUG 0
#include "lib_memory.h" #include "lib_memory.h"
#ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */ #ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */

@ -354,10 +354,6 @@ sendMessage(MmsConnection self, ByteBuffer* message)
} }
#endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */
#if (CONFIG_MMS_COLLECT_STATISTICS == 1)
self->statAplMessagesSent++;
#endif
IsoClientConnection_sendMessage(self->isoClient, message); IsoClientConnection_sendMessage(self->isoClient, message);
} }
@ -4383,6 +4379,69 @@ exit_function:
return; 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, &parameter);
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 struct writeMultipleVariablesParameter
{ {
Semaphore sem; Semaphore sem;

@ -529,6 +529,63 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const
return rval.encoded; 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 int
mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId,
uint32_t arrayIndex, const char* component, uint32_t arrayIndex, const char* component,

@ -763,8 +763,8 @@ MmsValue_setUtcTime(MmsValue* self, uint32_t timeval)
return self; return self;
} }
MmsValue* static void
MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) setUtcTimeMs(MmsValue* self, uint64_t timeval, uint8_t timeQuality)
{ {
uint32_t timeval32 = (timeval / 1000LL); uint32_t timeval32 = (timeval / 1000LL);
@ -786,7 +786,21 @@ MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval)
valueArray[6] = (fractionOfSecond & 0xff); valueArray[6] = (fractionOfSecond & 0xff);
/* encode time quality */ /* 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; 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); 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) if (bufPos >= bufferSize)
break; break;
@ -2226,9 +2240,13 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize)
int size = MmsValue_getBitStringSize(self); int size = MmsValue_getBitStringSize(self);
/* fill buffer with zeros */ /* fill buffer with zeros */
if (size > bufferSize) { if (size + 1 > bufferSize) {
memset(buffer, 0, bufferSize); memset(buffer, 0, bufferSize);
break;
size = bufferSize - 1;
if (size < 1)
break;
} }
int i; int i;

@ -1,7 +1,7 @@
/* /*
* mms_get_namelist_service.c * mms_get_namelist_service.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -190,20 +190,29 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName)
MmsDomain* domain = MmsDevice_getDomain(device, domainName); MmsDomain* domain = MmsDevice_getDomain(device, domainName);
if (domain != NULL) { if (domain) {
nameList = LinkedList_create();
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); MmsDomain* domain = MmsDevice_getDomain(device, domainName);
if (domain != NULL) { if (domain) {
nameList = LinkedList_create();
MmsVariableSpecification** variables = domain->namedVariables;
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) #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++) for (i = 0; i < domain->namedVariablesCount; i++)
index[i] = i; index[i] = i;
sortIndex(index, domain->namedVariablesCount, domain->namedVariables); sortIndex(index, domain->namedVariablesCount, domain->namedVariables);
#endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #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) #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 #else
element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name));
#endif #endif
#if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1)
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
char* prefix = variables[index[i]]->name; char* prefix = variables[index[i]]->name;
element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]);
#else #else
char* prefix = variables[i]->name; char* prefix = variables[i]->name;
element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); element = addSubNamedVaribleNamesToList(element, prefix, variables[i]);
#endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */
#endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */
} }
#if (CONFIG_MMS_SORT_NAME_LIST == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
GLOBAL_FREEMEM(index); GLOBAL_FREEMEM(index);
#endif #endif
}
} }
return nameList; return nameList;
@ -293,10 +312,19 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain
MmsDomain* domain = MmsDevice_getDomain(device, domainName); MmsDomain* domain = MmsDevice_getDomain(device, domainName);
if (domain != NULL) { if (domain) {
LinkedList variableLists = MmsDomain_getNamedVariableLists(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; return nameList;
@ -613,38 +641,79 @@ mmsServer_handleGetNameListRequest(
if (objectClass == OBJECT_CLASS_DOMAIN) { 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) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
StringUtils_sortList(nameList); StringUtils_sortList(nameList);
#endif #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) #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1)
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { 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) */ #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */
#if (MMS_DATA_SET_SERVICE == 1) #if (MMS_DATA_SET_SERVICE == 1)
else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { 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) #if (CONFIG_MMS_SORT_NAME_LIST == 1)
StringUtils_sortList(nameList); StringUtils_sortList(nameList);
#endif #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) */ #endif /* (MMS_DATA_SET_SERVICE == 1) */

@ -462,6 +462,20 @@ mmsServer_handleReadJournalRequest(
if (DEBUG_MMS_SERVER) if (DEBUG_MMS_SERVER)
printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name); 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; struct sJournalEncoder encoder;
encoder.buffer = response->buffer; encoder.buffer = response->buffer;

@ -3,22 +3,22 @@
* *
* Copyright 2013 Michael Zillgith * 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 * libIEC61850 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* libIEC61850 is distributed in the hope that it will be useful, * libIEC61850 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with libIEC61850. If not, see <http://www.gnu.org/licenses/>. * along with libIEC61850. If not, see <http://www.gnu.org/licenses/>.
* *
* See COPYING file for the complete license text. * See COPYING file for the complete license text.
*/ */
#include "libiec61850_platform_includes.h" #include "libiec61850_platform_includes.h"
@ -51,7 +51,6 @@ MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self)
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }
MmsDomain* MmsDomain*
MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self) MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self)
{ {
@ -120,5 +119,3 @@ MmsNamedVariableList_destroy(MmsNamedVariableList self)
GLOBAL_FREEMEM(self->name); GLOBAL_FREEMEM(self->name);
GLOBAL_FREEMEM(self); GLOBAL_FREEMEM(self);
} }

File diff suppressed because it is too large Load Diff

@ -1,7 +1,7 @@
/* /*
* mms_read_service.c * mms_read_service.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -695,13 +695,9 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable
LinkedList /*<MmsValue>*/ values = LinkedList_create(); LinkedList /*<MmsValue>*/ values = LinkedList_create();
LinkedList variables = MmsNamedVariableList_getVariableList(namedList); LinkedList variables = MmsNamedVariableList_getVariableList(namedList);
int variableCount = LinkedList_size(variables);
int i;
LinkedList variable = LinkedList_getNext(variables); LinkedList variable = LinkedList_getNext(variables);
for (i = 0; i < variableCount; i++) { while (variable) {
MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data; MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data;
@ -768,9 +764,21 @@ handleReadNamedVariableListRequest(
else { else {
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
if (namedList != NULL) { if (namedList)
createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), {
&accessSpec);
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 { else {
if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s not found!\n", nameIdStr); 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); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else { 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.isNamedVariableList = true;
accessSpec.specific = 0; accessSpec.specific = 0;
accessSpec.domainId = NULL; accessSpec.domainId = NULL;
accessSpec.itemId = listName; 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) #if (MMS_DYNAMIC_DATA_SETS == 1)
@ -815,14 +833,25 @@ handleReadNamedVariableListRequest(
if (namedList == NULL) if (namedList == NULL)
mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT);
else { else {
VarAccessSpec accessSpec;
accessSpec.isNamedVariableList = true; MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection);
accessSpec.specific = 2;
accessSpec.domainId = NULL; if (accessError == MMS_ERROR_NONE) {
accessSpec.itemId = listName;
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) */ #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */

@ -1,7 +1,7 @@
/* /*
* mms_server.c * mms_server.c
* *
* Copyright 2013-2020 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -95,13 +95,13 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration)
if (self->valueCaches == NULL) if (self->valueCaches == NULL)
goto exit_error; goto exit_error;
self->isLocked = false;
self->transmitBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE); self->transmitBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE);
if (self->transmitBuffer == NULL) if (self->transmitBuffer == NULL)
goto exit_error; goto exit_error;
self->blockRequests = false;
#if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1)
self->fileServiceEnabled = true; self->fileServiceEnabled = true;
self->dynamicVariableListServiceEnabled = true; self->dynamicVariableListServiceEnabled = true;
@ -359,10 +359,24 @@ MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connecti
} }
void 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->readJournalHandler = handler;
self->variableListChangedHandlerParameter = parameter; self->readJournalHandlerParameter = parameter;
}
void
MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter)
{
self->getNameListHandler = handler;
self->getNameListHandlerParameter = parameter;
} }
void void
@ -565,7 +579,6 @@ exit_function:
return value; return value;
} }
MmsDevice* MmsDevice*
MmsServer_getDevice(MmsServer self) MmsServer_getDevice(MmsServer self)
{ {
@ -820,4 +833,8 @@ MmsServer_getFilesystemBasepath(MmsServer self)
#endif #endif
} }
void
MmsServer_ignoreClientRequests(MmsServer self, bool enable)
{
self->blockRequests = enable;
}

@ -391,7 +391,7 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char
LinkedList element = LinkedList_getNext(namedVariableLists); LinkedList element = LinkedList_getNext(namedVariableLists);
while (element != NULL) { while (element) {
MmsNamedVariableList varList = (MmsNamedVariableList) element->data; MmsNamedVariableList varList = (MmsNamedVariableList) element->data;
if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) { if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) {
@ -405,14 +405,13 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char
return variableList; return variableList;
} }
void void
mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName) mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName)
{ {
LinkedList previousElement = namedVariableLists; LinkedList previousElement = namedVariableLists;
LinkedList element = LinkedList_getNext(namedVariableLists); LinkedList element = LinkedList_getNext(namedVariableLists);
while (element != NULL ) { while (element) {
MmsNamedVariableList varList = (MmsNamedVariableList) element->data; MmsNamedVariableList varList = (MmsNamedVariableList) element->data;
if (strcmp(MmsNamedVariableList_getName(varList), variableListName) if (strcmp(MmsNamedVariableList_getName(varList), variableListName)
@ -428,5 +427,3 @@ mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListNa
element = LinkedList_getNext(element); element = LinkedList_getNext(element);
} }
} }

@ -665,6 +665,9 @@ handleConfirmedResponsePdu(
static inline void static inline void
MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response) MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response)
{ {
if (self->server->blockRequests)
return;
uint8_t* buffer = message->buffer; uint8_t* buffer = message->buffer;
if (message->size < 2) if (message->size < 2)

@ -1,7 +1,7 @@
/* /*
* mms_write_service.c * mms_write_service.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -430,8 +430,19 @@ handleWriteNamedVariableListRequest(
else { else {
MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr);
if (namedList != NULL) { if (namedList) {
createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
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 { else {
if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s not found!\n", nameIdStr); 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); MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName);
if (namedList != NULL) { if (namedList) {
createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
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 { else {
if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s not found!\n", listName); 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); MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName);
if (namedList != NULL) { if (namedList) {
createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response);
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 { else {
if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s not found!\n", listName); 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 void
mmsServer_handleWriteRequest( mmsServer_handleWriteRequest(
MmsServerConnection connection, MmsServerConnection connection,
@ -603,15 +688,21 @@ mmsServer_handleWriteRequest(
AlternateAccess_t* alternateAccess = varSpec->alternateAccess; AlternateAccess_t* alternateAccess = varSpec->alternateAccess;
if (alternateAccess != NULL) { 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; accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
continue; continue;
} }
if (!mmsServer_isIndexAccess(alternateAccess)) { if ((variable->type == MMS_ARRAY) && (mmsServer_isIndexAccess(alternateAccess) == false)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED;
continue; 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]; Data_t* dataElement = writeRequest->listOfData.list.array[i];
@ -628,72 +719,84 @@ mmsServer_handleWriteRequest(
if (domain == NULL) if (domain == NULL)
domain = (MmsDomain*) device; domain = (MmsDomain*) device;
MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr); if (mmsServer_isIndexAccess(alternateAccess)) {
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 (elementValue == NULL) { if (cachedArray == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop; goto end_of_main_loop;
} }
if (mmsServer_isAccessToArrayComponent(alternateAccess)) { int index = mmsServer_getLowIndex(alternateAccess);
MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); int numberOfElements = mmsServer_getNumberOfElements(alternateAccess);
if (namedVariable) { if (numberOfElements == 0) { /* select single array element with index */
elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue);
} MmsValue* elementValue = MmsValue_getElement(cachedArray, index);
if ((namedVariable == NULL) || (elementValue == NULL)) { if (elementValue == NULL) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT;
goto end_of_main_loop; goto end_of_main_loop;
} }
}
if (MmsValue_update(elementValue, value) == false) { if (mmsServer_isAccessToArrayComponent(alternateAccess)) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr);
goto end_of_main_loop;
}
}
else { /* select sub-array with start-index and number-of-elements */
if (MmsValue_getType(value) != MMS_ARRAY) { if (namedVariable) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue);
goto end_of_main_loop; }
}
int elementNo; if ((namedVariable == NULL) || (elementValue == NULL)) {
accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT;
for (elementNo = 0; elementNo < numberOfElements; elementNo++) { goto end_of_main_loop;
MmsValue* newElement = MmsValue_getElement(value, elementNo); }
MmsValue* elementValue = MmsValue_getElement(cachedArray, index++); }
if ((elementValue == NULL) || (newElement == NULL) ) { if (MmsValue_update(elementValue, value) == false) {
accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop; 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; accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT;
goto end_of_main_loop; 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; accessResults[i] = DATA_ACCESS_ERROR_SUCCESS;
goto end_of_main_loop; 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 */ /* Check for correct type */
@ -723,7 +826,7 @@ mmsServer_handleWriteRequest(
goto exit_function; goto exit_function;
} }
exit_function: exit_function:
MmsServer_unlockModel(connection->server); MmsServer_unlockModel(connection->server);

@ -1,7 +1,7 @@
/* /*
* iso_connection.c * iso_connection.c
* *
* Copyright 2013-2022 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *
@ -160,6 +160,8 @@ IsoConnection_removeFromHandleSet(const IsoConnection self, HandleSet handles)
void void
IsoConnection_callTickHandler(IsoConnection self) IsoConnection_callTickHandler(IsoConnection self)
{ {
CotpConnection_flushBuffer(self->cotpConnection);
if (self->tickHandler) { if (self->tickHandler) {
self->tickHandler(self->handlerParameter); self->tickHandler(self->handlerParameter);
} }
@ -171,10 +173,7 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread)
#if (CONFIG_MMS_SINGLE_THREADED != 1) #if (CONFIG_MMS_SINGLE_THREADED != 1)
if (isSingleThread == false) { if (isSingleThread == false) {
/* call tick handler */ IsoConnection_callTickHandler(self);
if (self->tickHandler) {
self->tickHandler(self->handlerParameter);
}
if (Handleset_waitReady(self->handleSet, 10) < 1) if (Handleset_waitReady(self->handleSet, 10) < 1)
goto exit_function; goto exit_function;

@ -455,7 +455,6 @@ exit_function:
return success; return success;
} }
/** used by single and multi-threaded versions /** used by single and multi-threaded versions
* *
* \param isSingleThread when true server is running in single thread or non-thread mode * \param isSingleThread when true server is running in single thread or non-thread mode

@ -3,7 +3,7 @@
* *
* Implementation of RSessionCrypto interface using mbedtls * Implementation of RSessionCrypto interface using mbedtls
* *
* Copyright 2013-2021 Michael Zillgith * Copyright 2013-2023 Michael Zillgith
* *
* This file is part of libIEC61850. * This file is part of libIEC61850.
* *

@ -1,9 +1,9 @@
README 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.

Loading…
Cancel
Save