From b3dbba847b7f71e36e639b2b6b98d852ac49acd5 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 10 Nov 2022 11:45:21 +0000 Subject: [PATCH 001/214] - fixed - endless loop sending reports when MMS PDU size is too small (LIB61850-365) --- src/iec61850/server/mms_mapping/reporting.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index d75bf137..c44c2791 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -3339,6 +3339,8 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue* subSeqNum = self->subSeqVal; + int numberOfAddedElements = 0; + for (i = 0; i < self->dataSet->elementCount; i++) { if ((report->flags > 0) || MmsValue_getBitStringBit(inclusionField, i)) { @@ -3420,6 +3422,8 @@ sendNextReportEntrySegment(ReportControl* self) MmsValue_setBitStringBit(self->inclusionField, i, true); + numberOfAddedElements++; + accessResultSize += elementSize; estimatedSegmentSize += elementSize; } @@ -3460,11 +3464,16 @@ sendNextReportEntrySegment(ReportControl* self) uint32_t informationReportSize = 1 + informationReportContentSize + BerEncoder_determineLengthSize(informationReportContentSize); uint32_t completeMessageSize = 1 + informationReportSize + BerEncoder_determineLengthSize(informationReportSize); - if ((int) completeMessageSize > maxMmsPduSize) { + if (((int) completeMessageSize > maxMmsPduSize) || (numberOfAddedElements == 0)) { if (DEBUG_IED_SERVER) - printf("IED_SERVER: report message too large %u (max = %i) -> skip message!\n", completeMessageSize, maxMmsPduSize); + printf("IED_SERVER: MMS PDU size too small to encode report data (max PDU size = %i) -> skip message!\n", maxMmsPduSize); - goto exit_function; + self->startIndexForNextSegment = 0; + segmented = false; + moreFollows = false; + sentSuccess = true; + + goto exit_remove_report; } /* encode the report message */ @@ -3688,6 +3697,8 @@ sendNextReportEntrySegment(ReportControl* self) self->startIndexForNextSegment = maxIndex; } +exit_remove_report: + if (segmented == false) { assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next); From f61c58dd35038b8326ad2cf680c8fdfdb736374c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 11 Nov 2022 19:00:22 +0000 Subject: [PATCH 002/214] - added TLSConnection object to provide more context in TLS event callback (LIB61850-366) --- dotnet/IEC61850forCSharp/TLS.cs | 1 - .../tls_client_example/tls_client_example.c | 8 +- .../tls_server_example/tls_server_example.c | 10 +- hal/inc/hal_socket.h | 2 +- hal/inc/tls_config.h | 69 ++++++++++--- hal/tls/mbedtls/tls_mbedtls.c | 99 ++++++++++++++----- 6 files changed, 148 insertions(+), 41 deletions(-) diff --git a/dotnet/IEC61850forCSharp/TLS.cs b/dotnet/IEC61850forCSharp/TLS.cs index 69a273b0..da7b8d73 100644 --- a/dotnet/IEC61850forCSharp/TLS.cs +++ b/dotnet/IEC61850forCSharp/TLS.cs @@ -130,7 +130,6 @@ namespace IEC61850 } } - public void SetClientMode() { TLSConfiguration_setClientMode (self); diff --git a/examples/tls_client_example/tls_client_example.c b/examples/tls_client_example/tls_client_example.c index c546f425..5dd982d2 100644 --- a/examples/tls_client_example/tls_client_example.c +++ b/examples/tls_client_example/tls_client_example.c @@ -30,11 +30,15 @@ reportCallbackFunction(void* parameter, ClientReport report) } static void -securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg) +securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) { (void)parameter; - printf("[SECURITY EVENT] %s (t: %i, c: %i)\n", msg, eventLevel, eventCode); + char* peerAddr = TLSConnection_getPeerAddress(con, NULL); + + printf("[SECURITY EVENT] %s (%s)(t: %i, c: %i)\n", msg, peerAddr, eventLevel, eventCode); + + free(peerAddr); } int main(int argc, char** argv) { diff --git a/examples/tls_server_example/tls_server_example.c b/examples/tls_server_example/tls_server_example.c index 08bfd662..e2324faf 100644 --- a/examples/tls_server_example/tls_server_example.c +++ b/examples/tls_server_example/tls_server_example.c @@ -103,11 +103,17 @@ clientAuthenticator(void* parameter, AcseAuthenticationParameter authParameter, } static void -securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg) +securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) { (void)parameter; - printf("[SECURITY EVENT] %s (t: %i, c: %i)\n", msg, eventLevel, eventCode); + char* peerAddr = TLSConnection_getPeerAddress(con, NULL); + + const char* tlsVersionStr = TLSConfigVersion_toString(TLSConnection_getTLSVersion(con)); + + printf("[SECURITY EVENT - %s] %s (%s)(t: %i, c: %i)\n", tlsVersionStr, msg, peerAddr, eventLevel, eventCode); + + free(peerAddr); } int diff --git a/hal/inc/hal_socket.h b/hal/inc/hal_socket.h index 2f681cce..bddb98f1 100644 --- a/hal/inc/hal_socket.h +++ b/hal/inc/hal_socket.h @@ -322,7 +322,7 @@ Socket_getPeerAddress(Socket self); * * The peer address has to be returned as null terminated string * - * Implementation of this function is MANDATORY (lib60870) + * Implementation of this function is MANDATORY (lib60870 and libiec61850) * * \param self the client, connection or server socket instance * \param peerAddressString a string to store the peer address (the string should have space diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index 37af40c2..9a456752 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -3,7 +3,7 @@ * * TLS Configuration API for protocol stacks using TCP/IP * - * Copyright 2017-2021 Michael Zillgith + * Copyright 2017-2022 Michael Zillgith * * Abstraction layer for configuration of different TLS implementations * @@ -50,6 +50,25 @@ TLSConfiguration_create(void); PAL_API void TLSConfiguration_setClientMode(TLSConfiguration self); +typedef enum { + TLS_VERSION_NOT_SELECTED = 0, + TLS_VERSION_SSL_3_0 = 3, + TLS_VERSION_TLS_1_0 = 4, + TLS_VERSION_TLS_1_1 = 5, + TLS_VERSION_TLS_1_2 = 6, + TLS_VERSION_TLS_1_3 = 7 +} TLSConfigVersion; + +/** + * \brief Convert TLS version number to string + * + * \param version TLS version number + * + * \return the TLS version as null terminated string + */ +PAL_API const char* +TLSConfigVersion_toString(TLSConfigVersion version); + typedef enum { TLS_SEC_EVT_INFO = 0, TLS_SEC_EVT_WARNING = 1, @@ -71,10 +90,47 @@ typedef enum { #define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13 #define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 12 -typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* message); +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); + +/** + * \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, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* message, TLSConnection con); /** * \brief Set the security event handler + * + * \param handler the security event callback handler + * \param parameter user provided parameter to be passed to the callback handler */ PAL_API void TLSConfiguration_setEventHandler(TLSConfiguration self, TLSConfiguration_EventHandler handler, void* parameter); @@ -209,15 +265,6 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil PAL_API void TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs); -typedef enum { - TLS_VERSION_NOT_SELECTED = 0, - TLS_VERSION_SSL_3_0 = 3, - TLS_VERSION_TLS_1_0 = 4, - TLS_VERSION_TLS_1_1 = 5, - TLS_VERSION_TLS_1_2 = 6, - TLS_VERSION_TLS_1_3 = 7 -} TLSConfigVersion; - /** * \brief Set minimal allowed TLS version to use */ diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index 65c96538..f7d7127e 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -105,10 +105,10 @@ struct sTLSSocket { }; static void -raiseSecurityEvent(TLSConfiguration config, TLSConfiguration_EventLevel eventCategory, int eventCode, const char* message) +raiseSecurityEvent(TLSConfiguration config, TLSConfiguration_EventLevel eventCategory, int eventCode, const char* message, TLSSocket socket) { if (config->eventHandler) { - config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message); + config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message, (TLSConnection)socket); } } @@ -174,7 +174,7 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth if (certMatches) *flags = 0; else { - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured"); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured", self); *flags |= MBEDTLS_X509_BADCERT_OTHER; return 1; @@ -518,7 +518,7 @@ TLSConfiguration_destroy(TLSConfiguration self) } static void -createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags) +createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket socket) { if (config->eventHandler == NULL) return; @@ -526,55 +526,55 @@ createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags) switch (ret) { 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, "Incident: Algorithm not supported", socket); break; case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Incident: Unsecure communication"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Incident: Unsecure communication", socket); break; case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Incident: Certificate unavailable"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Incident: Certificate unavailable", socket); break; case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Incident: Bad certificate"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Incident: Bad certificate", socket); break; case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Incident: TLS certificate size exceeded"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Incident: TLS certificate size exceeded", socket); break; case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: certificate validation: certificate signature could not be validated"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: certificate validation: certificate signature could not be validated", socket); break; case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Incident: Certificate required"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Incident: Certificate required", socket); break; case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: { if (flags & MBEDTLS_X509_BADCERT_EXPIRED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Incident: Certificate expired"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Incident: Certificate expired", socket); } else if (flags & MBEDTLS_X509_BADCERT_REVOKED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Incident: Certificate revoked"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Incident: Certificate revoked", socket); } else if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Incident: Certificate validation: CA certificate not available"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Incident: Certificate validation: CA certificate not available", socket); } else if (flags & MBEDTLS_X509_BADCERT_OTHER) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured", socket); } else { - 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_VALIDATION_FAILED, "Incident: Certificate verification failed", socket); } } break; default: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Incident: handshake failed for unknown reason"); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Incident: handshake failed for unknown reason", socket); break; } } @@ -753,7 +753,7 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); - createSecurityEvents(configuration, ret, flags); + createSecurityEvents(configuration, ret, flags, self); mbedtls_ssl_free(&(self->ssl)); @@ -793,7 +793,7 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient self->lastRenegotiationTime = Hal_getTimeInMs(); if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { - raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version"); + raiseSecurityEvent(configuration, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self); } } @@ -817,7 +817,7 @@ TLSSocket_performHandshake(TLSSocket self) if (ret == 0) { if (getTLSVersion(self->ssl.major_ver, self->ssl.minor_ver) < TLS_VERSION_TLS_1_2) { - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version"); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_WARNING, TLS_EVENT_CODE_WRN_INSECURE_TLS_VERSION, "Warning: Insecure TLS version", self); } return true; @@ -828,7 +828,7 @@ TLSSocket_performHandshake(TLSSocket self) if (self->tlsConfig->eventHandler) { uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); - createSecurityEvents(self->tlsConfig, ret, flags); + createSecurityEvents(self->tlsConfig, ret, flags, self); } return false; @@ -849,7 +849,7 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size) 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"); + 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"); @@ -885,7 +885,7 @@ TLSSocket_read(TLSSocket self, uint8_t* buf, int size) { uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); - createSecurityEvents(self->tlsConfig, ret, flags); + createSecurityEvents(self->tlsConfig, ret, flags, self); } return -1; @@ -912,7 +912,7 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size) 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"); + 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"); @@ -970,3 +970,54 @@ TLSSocket_close(TLSSocket self) GLOBAL_FREEMEM(self); } + +char* +TLSConnection_getPeerAddress(TLSConnection self, char* peerAddrBuf) +{ + TLSSocket socket = (TLSSocket)self; + + if (peerAddrBuf == NULL) { + peerAddrBuf = (char*)GLOBAL_MALLOC(61); + } + + if (peerAddrBuf) + return Socket_getPeerAddressStatic(socket->socket, peerAddrBuf); + else + return NULL; +} + +uint8_t* +TLSConnection_getPeerCertificate(TLSConnection self, int* certSize) +{ + TLSSocket socket = (TLSSocket)self; + + return TLSSocket_getPeerCertificate(socket, certSize); +} + +TLSConfigVersion +TLSConnection_getTLSVersion(TLSConnection self) +{ + TLSSocket socket = (TLSSocket)self; + + return getTLSVersion(socket->ssl.major_ver, socket->ssl.minor_ver); +} + +const char* +TLSConfigVersion_toString(TLSConfigVersion version) +{ + switch (version) + { + case TLS_VERSION_SSL_3_0: + return "SSL 3.0"; + case TLS_VERSION_TLS_1_0: + return "TLS 1.0"; + case TLS_VERSION_TLS_1_1: + return "TLS 1.1"; + case TLS_VERSION_TLS_1_2: + return "TLS 1.2"; + case TLS_VERSION_TLS_1_3: + return "TLS 1.3"; + default: + return "unknown TLS version"; + } +} From bfc0e213c7d232ec6ae738f5fdeb2ee219044a1b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 12 Nov 2022 07:20:26 +0000 Subject: [PATCH 003/214] - removed deprecated compatibility defined for very old library versions --- config/stack_config.h | 3 -- config/stack_config.h.cmake | 3 -- src/iec61850/inc/iec61850_common.h | 19 ---------- src/iec61850/inc/iec61850_model.h | 38 ------------------- .../iso_mms/client/mms_client_connection.c | 4 -- 5 files changed, 67 deletions(-) diff --git a/config/stack_config.h b/config/stack_config.h index 8a71f0c3..2559069c 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -229,9 +229,6 @@ #define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 -/* use short FC defines as in old API */ -#define CONFIG_PROVIDE_OLD_FC_DEFINES 0 - /* Support user access to raw messages */ #define CONFIG_MMS_RAW_MESSAGE_LOGGING 1 diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index d3ef67b5..c14b0c20 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -215,9 +215,6 @@ #define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 -/* use short FC defines as in old API */ -#define CONFIG_PROVIDE_OLD_FC_DEFINES 0 - /* Support user acccess to raw messages */ #cmakedefine01 CONFIG_MMS_RAW_MESSAGE_LOGGING diff --git a/src/iec61850/inc/iec61850_common.h b/src/iec61850/inc/iec61850_common.h index c2765c45..9a364656 100644 --- a/src/iec61850/inc/iec61850_common.h +++ b/src/iec61850/inc/iec61850_common.h @@ -241,25 +241,6 @@ typedef enum { * @{ */ -#if (CONFIG_PROVIDE_OLD_FC_DEFINES == 1) -#define ST IEC61850_FC_ST -#define MX IEC61850_FC_MX -#define SP IEC61850_FC_SP -#define SV IEC61850_FC_SV -#define CF IEC61850_FC_CF -#define DC IEC61850_FC_DC -#define SG IEC61850_FC_SG -#define SE IEC61850_FC_SE -#define SR IEC61850_FC_SR -#define OR IEC61850_FC_OR -#define BL IEC61850_FC_BL -#define EX IEC61850_FC_EX -#define CO IEC61850_FC_CO -#define ALL IEC61850_FC_ALL -#define NONE IEC61850_FC_NONE -#endif /* (CONFIG_PROVIDE_OLD_FC_DEFINES == 1) */ - - /** FCs (Functional constraints) according to IEC 61850-7-2 */ typedef enum eFunctionalConstraint { /** Status information */ diff --git a/src/iec61850/inc/iec61850_model.h b/src/iec61850/inc/iec61850_model.h index 750f47ac..a7ea685e 100644 --- a/src/iec61850/inc/iec61850_model.h +++ b/src/iec61850/inc/iec61850_model.h @@ -121,44 +121,6 @@ typedef enum { IEC61850_CURRENCY = 30, IEC61850_OPTFLDS = 31, /* bit-string(10) */ IEC61850_TRGOPS = 32 /* bit-string(6) */ - - -#if (CONFIG_IEC61850_USE_COMPAT_TYPE_DECLARATIONS == 1) - , - BOOLEAN = 0,/* int */ - INT8 = 1, /* int8_t */ - INT16 = 2, /* int16_t */ - INT32 = 3, /* int32_t */ - INT64 = 4, /* int64_t */ - INT128 = 5, /* no native mapping! */ - INT8U = 6, /* uint8_t */ - INT16U = 7, /* uint16_t */ - INT24U = 8, /* uint32_t */ - INT32U = 9, /* uint32_t */ - FLOAT32 = 10, /* float */ - FLOAT64 = 11, /* double */ - ENUMERATED = 12, - OCTET_STRING_64 = 13, - OCTET_STRING_6 = 14, - OCTET_STRING_8 = 15, - VISIBLE_STRING_32 = 16, - VISIBLE_STRING_64 = 17, - VISIBLE_STRING_65 = 18, - VISIBLE_STRING_129 = 19, - VISIBLE_STRING_255 = 20, - UNICODE_STRING_255 = 21, - TIMESTAMP = 22, - QUALITY = 23, - CHECK = 24, - CODEDENUM = 25, - GENERIC_BITSTRING = 26, - CONSTRUCTED = 27, - ENTRY_TIME = 28, - PHYCOMADDR = 29, - CURRENCY = 30 - OPTFLDS = 31, - TRGOPS = 32 -#endif } DataAttributeType; typedef enum { diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index de7db996..63b50b5f 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -354,10 +354,6 @@ sendMessage(MmsConnection self, ByteBuffer* message) } #endif /* (CONFIG_MMS_RAW_MESSAGE_LOGGING == 1) */ -#if (CONFIG_MMS_COLLECT_STATISTICS == 1) - self->statAplMessagesSent++; -#endif - IsoClientConnection_sendMessage(self->isoClient, message); } From 2f3c7cae61aa618f94b6078c2e66372d1a12f82e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 12 Nov 2022 07:22:33 +0000 Subject: [PATCH 004/214] - added -Wundef compiler flag and removed some of the resulting warnings --- CMakeLists.txt | 5 +++++ src/iec61850/client/client_control.c | 2 +- src/mms/iso_mms/asn1c/asn_internal.h | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 618916e9..6172e302 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,11 @@ if (SUPPORT_REDUNDANT_DECLS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls") endif(SUPPORT_REDUNDANT_DECLS) +check_c_compiler_flag("-Wundef" SUPPORT_UNDEF) +if (SUPPORT_UNDEF) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wundef") +endif(SUPPORT_UNDEF) + # write the detected stuff to this file configure_file( ${CMAKE_CURRENT_LIST_DIR}/config/stack_config.h.cmake diff --git a/src/iec61850/client/client_control.c b/src/iec61850/client/client_control.c index caa07148..ee9ec9e9 100644 --- a/src/iec61850/client/client_control.c +++ b/src/iec61850/client/client_control.c @@ -29,7 +29,7 @@ #include "mms_client_connection.h" #include "ied_connection_private.h" -#if _MSC_VER +#ifdef _MSC_VER #define snprintf _snprintf #endif diff --git a/src/mms/iso_mms/asn1c/asn_internal.h b/src/mms/iso_mms/asn1c/asn_internal.h index 493b4336..5d6910d8 100644 --- a/src/mms/iso_mms/asn1c/asn_internal.h +++ b/src/mms/iso_mms/asn1c/asn_internal.h @@ -11,6 +11,8 @@ #include "asn_application.h" /* Application-visible API */ +#define EMIT_ASN_DEBUG 0 + #include "lib_memory.h" #ifndef __NO_ASSERT_H__ /* Include assert.h only for internal use. */ From 17a705d8c094c1a2e3518be19a63efc1a082a4df Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 17 Nov 2022 22:02:33 +0000 Subject: [PATCH 005/214] - fixed - servers sends object-access-unsupported on GetAllData when ReadAccessHandler is installed (LIB61850-370) --- src/iec61850/server/mms_mapping/mms_mapping.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 3247d4b1..3a716a27 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3317,18 +3317,16 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS { return DATA_ACCESS_ERROR_SUCCESS; } - else { - + else + { StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); - if (ln != NULL) { - - + if (ln != NULL) + { char* doStart = strchr(separator + 1, '$'); - if (doStart != NULL) { char* doEnd = strchr(doStart + 1, '$'); @@ -3361,6 +3359,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); + } } } } From a3a6f2c77fef7c21e89981bb3f9cbb2dfd87a052 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 21 Nov 2022 16:56:00 +0000 Subject: [PATCH 006/214] - IED Server: added function to set time quality for internally updated times (LIB61850-372) --- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 15 ++++++++++++ .../server_example_sg.c | 7 +++++- src/iec61850/inc/iec61850_server.h | 17 ++++++++++++-- src/iec61850/inc_private/ied_server_private.h | 2 ++ src/iec61850/server/impl/ied_server.c | 23 +++++++++++++++++-- src/iec61850/server/mms_mapping/control.c | 9 ++++---- src/iec61850/server/mms_mapping/logging.c | 2 +- src/iec61850/server/mms_mapping/mms_goose.c | 4 ++-- src/iec61850/server/mms_mapping/mms_mapping.c | 6 ++--- src/iec61850/server/mms_mapping/reporting.c | 2 +- src/mms/inc/mms_value.h | 20 +++++++++++++++- src/mms/iso_mms/common/mms_value.c | 20 +++++++++++++--- 12 files changed, 106 insertions(+), 21 deletions(-) diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index 5c6a40d6..aefd1c7b 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -2273,6 +2273,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedServer_setRCBEventHandler(IntPtr self, InternalRCBEventHandler handler, IntPtr parameter); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedServer_setTimeQuality(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool leapSecondKnown, [MarshalAs(UnmanagedType.I1)] bool clockFailure, [MarshalAs(UnmanagedType.I1)] bool clockNotSynchronized, int subsecondPrecision); + private IntPtr self = IntPtr.Zero; private InternalControlHandler internalControlHandlerRef = null; @@ -2996,6 +2999,18 @@ namespace IEC61850 } } + /// + /// Set the time quality for all timestamps internally generated by this IedServer instance + /// + /// set/unset leap seconds known flag + /// set/unset clock failure flag + /// set/unset clock not synchronized flag + /// set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp) + public void SetTimeQuality(bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision) + { + IedServer_setTimeQuality(self, leapSecondKnown, clockFailure, clockNotSynchronized, subsecondPrecision); + } + } } diff --git a/examples/server_example_setting_groups/server_example_sg.c b/examples/server_example_setting_groups/server_example_sg.c index 4466c1ad..48f8c4d5 100644 --- a/examples/server_example_setting_groups/server_example_sg.c +++ b/examples/server_example_setting_groups/server_example_sg.c @@ -97,7 +97,10 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu { void* securityToken = ClientConnection_getSecurityToken(connection); - printf("Read access to %s/%s.%s\n", ld->name, ln->name, dataObject->name); + if (dataObject) + printf("Read access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject->name, FunctionalConstraint_toString(fc)); + else + printf("Read access to %s/%s[%s]\n", ld->name, ln->name, FunctionalConstraint_toString(fc)); return DATA_ACCESS_ERROR_SUCCESS; } @@ -112,6 +115,8 @@ main(int argc, char** argv) iedServer = IedServer_createWithConfig(&iedModel, NULL, config); + IedServer_setTimeQuality(iedServer, true, false, false, 10); + IedServerConfig_destroy(config); LogicalDevice* ld = IEDMODEL_PROT; diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index b0097e3f..d206b904 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -687,6 +687,21 @@ IedServer_setGooseInterfaceIdEx(IedServer self, LogicalNode* ln, const char* gcb LIB61850_API void IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, bool useVlanTag); +/** + * \brief Set the time quality for all timestamps internally generated by this IedServer instance + * + * You can call this function during the initialization of the server or whenever a time quality + * flag has to be updated (on clock failure or change of time synchronization state). + * + * \param self the instance of IedServer to operate on. + * \param leapSecondKnown set/unset leap seconds known flag + * \param clockFailure set/unset clock failure flag + * \param clockNotSynchronized set/unset clock not synchronized flag + * \param subsecondPrecision set the subsecond precision (number of significant bits of the fractionOfSecond part of the time stamp) + */ +LIB61850_API void +IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision); + /**@}*/ /** @@ -710,8 +725,6 @@ IedServer_useGooseVlanTag(IedServer self, LogicalNode* ln, const char* gcbName, LIB61850_API void IedServer_setAuthenticator(IedServer self, AcseAuthenticator authenticator, void* authenticatorParameter); - - /** * \brief get the peer address of this connection as string * diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index 14a694b1..e6f6e412 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -78,6 +78,8 @@ struct sIedServer uint8_t edition; + uint8_t timeQuality; /* user settable time quality for internally updated times */ + bool running; }; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 70527812..9416d200 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -659,6 +659,7 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio } #endif + IedServer_setTimeQuality(self, true, false, false, 10); } else { IedServer_destroy(self); @@ -1479,7 +1480,7 @@ IedServer_updateUTCTimeAttributeValue(IedServer self, DataAttribute* dataAttribu #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->dataModelLock); #endif - MmsValue_setUtcTimeMs(dataAttribute->mmsValue, value); + MmsValue_setUtcTimeMsEx(dataAttribute->mmsValue, value, self->timeQuality); #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->dataModelLock); #endif @@ -1882,7 +1883,6 @@ private_IedServer_removeClientConnection(IedServer self, ClientConnection client #endif } - void IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) { @@ -1890,3 +1890,22 @@ IedServer_setGooseInterfaceId(IedServer self, const char* interfaceId) self->mmsMapping->gooseInterfaceId = StringUtils_copyString(interfaceId); #endif } + +void +IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure, bool clockNotSynchronized, int subsecondPrecision) +{ + uint8_t timeQuality = 0; + + if (clockNotSynchronized) + timeQuality += 0x20; + + if (clockFailure) + timeQuality += 0x40; + + if (leapSecondKnown) + timeQuality += 0x80; + + timeQuality += (subsecondPrecision & 0x1f); + + self->timeQuality = timeQuality; +} diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index dc2ae540..ba7c5a64 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -258,7 +258,7 @@ copyControlValuesToTrackingObject(MmsMapping* self, ControlObject* controlObject MmsValue_update(trkInst->ctlNum->mmsValue, controlObject->ctlNum); if (trkInst->operTm) - MmsValue_setUtcTimeMs(trkInst->operTm->mmsValue, controlObject->operateTime); + MmsValue_setUtcTimeMsEx(trkInst->operTm->mmsValue, controlObject->operateTime, self->iedServer->timeQuality); if (trkInst->respAddCause) MmsValue_update(trkInst->respAddCause->mmsValue, controlObject->addCause); @@ -394,7 +394,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ControlObject* controlObject MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, errVal); @@ -590,9 +590,7 @@ setOpOk(ControlObject* self, bool value, uint64_t currentTimeInMs) if (self->tOpOk) { MmsValue* timestamp = self->tOpOk->mmsValue; - MmsValue_setUtcTimeMs(timestamp, currentTimeInMs); - - /* TODO update time quality */ + MmsValue_setUtcTimeMsEx(timestamp, currentTimeInMs, self->iedServer->timeQuality); } self->pendingEvents |= PENDING_EVENT_OP_OK_TRUE; @@ -862,6 +860,7 @@ executeStateMachine: MmsValue* operTm = getOperParameterOperTime(controlObject->oper); MmsValue_setUtcTime(operTm, 0); + MmsValue_setUtcTimeQuality(operTm, self->iedServer->timeQuality); } else { MmsServerConnection_sendWriteResponse(controlObject->mmsConnection, controlObject->operateInvokeId, diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index 6fc8f7b4..e841feb2 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -418,7 +418,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, LogControl* logControl, IEC6 MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index a5352b00..5aab2585 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -1,7 +1,7 @@ /* * mms_goose.c * - * Copyright 2013-2021 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -231,7 +231,7 @@ updateGenericTrackingObjectValues(MmsGooseControlBlock gc, IEC61850_ServiceType MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), gc->mmsMapping->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 3a716a27..fc080ea2 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -693,7 +693,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, SettingGroupControlBlock* sg MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, @@ -840,7 +840,7 @@ MmsMapping_changeActiveSettingGroup(MmsMapping* self, SettingGroupControlBlock* MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue_setUint8(actSg, sgcb->actSG); - MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality); #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) copySGCBValuesToTrackingObject(self, sgcb); @@ -2720,7 +2720,7 @@ mmsWriteHandler(void* parameter, MmsDomain* domain, MmsValue* lActTm = MmsValue_getElement(sg->sgcbMmsValues, 4); MmsValue_setUint8(actSg, sg->sgcb->actSG); - MmsValue_setUtcTimeMs(lActTm, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(lActTm, Hal_getTimeInMs(), self->iedServer->timeQuality); } else retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index c44c2791..3b170a54 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -579,7 +579,7 @@ updateGenericTrackingObjectValues(MmsMapping* self, ReportControl* rc, IEC61850_ MmsValue_setInt32(trkInst->serviceType->mmsValue, (int) serviceType); if (trkInst->t) - MmsValue_setUtcTimeMs(trkInst->t->mmsValue, Hal_getTimeInMs()); + MmsValue_setUtcTimeMsEx(trkInst->t->mmsValue, Hal_getTimeInMs(), self->iedServer->timeQuality); if (trkInst->errorCode) MmsValue_setInt32(trkInst->errorCode->mmsValue, diff --git a/src/mms/inc/mms_value.h b/src/mms/inc/mms_value.h index e67bccb2..206345c4 100644 --- a/src/mms/inc/mms_value.h +++ b/src/mms/inc/mms_value.h @@ -498,12 +498,30 @@ MmsValue_getUtcTimeInMsWithUs(const MmsValue* self, uint32_t* usec); * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time) * * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME. - * * \param timeQuality the byte representing the time quality */ LIB61850_API void MmsValue_setUtcTimeQuality(MmsValue* self, uint8_t timeQuality); +/** + * \brief Update an MmsValue object of type MMS_UTCTIME with a millisecond time. + * + * Meaning of the bits in the timeQuality byte: + * + * bit 7 = leapSecondsKnown + * bit 6 = clockFailure + * bit 5 = clockNotSynchronized + * bit 0-4 = subsecond time accuracy (number of significant bits of subsecond time) + * + * \param self MmsValue instance to operate on. Has to be of a type MMS_UTCTIME. + * \param timeval the new value in milliseconds since epoch (1970/01/01 00:00 UTC) + * \param timeQuality the byte representing the time quality + * + * \return the updated MmsValue instance + */ +LIB61850_API MmsValue* +MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality); + /** * \brief get the TimeQuality byte of the UtcTime * diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index 5d80a630..29e96e4d 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -763,8 +763,8 @@ MmsValue_setUtcTime(MmsValue* self, uint32_t timeval) return self; } -MmsValue* -MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) +static void +setUtcTimeMs(MmsValue* self, uint64_t timeval, uint8_t timeQuality) { uint32_t timeval32 = (timeval / 1000LL); @@ -786,7 +786,21 @@ MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) valueArray[6] = (fractionOfSecond & 0xff); /* encode time quality */ - valueArray[7] = 0x0a; /* 10 bit sub-second time accuracy */ + valueArray[7] = timeQuality; +} + +MmsValue* +MmsValue_setUtcTimeMs(MmsValue* self, uint64_t timeval) +{ + setUtcTimeMs(self, timeval, 0x0a); /* set quality as 10 bit sub-second time accuracy */ + + return self; +} + +MmsValue* +MmsValue_setUtcTimeMsEx(MmsValue* self, uint64_t timeval, uint8_t timeQuality) +{ + setUtcTimeMs(self, timeval, timeQuality); return self; } From 1998680914a1ac190c878c325c87ba1bbe40947a Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 6 Dec 2022 07:47:49 +0000 Subject: [PATCH 007/214] - replaced all control data access errors with codes allowed in 8-1:2011 table 87 (LIB61850-375) --- src/iec61850/server/mms_mapping/control.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index ba7c5a64..15cb6a32 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -1659,8 +1659,6 @@ ControlObject_sendCommandTerminationNegative(ControlObject* self) MmsServerConnection_sendInformationReportListOfVariables(self->mmsConnection, &_varSpecList, &_values, false); } /* ControlObject_sendCommandTerminationNegative() */ - - void ControlObject_sendLastApplError(ControlObject* self, MmsServerConnection connection, char* ctlVariable, int error, ControlAddCause addCause, MmsValue* ctlNum, MmsValue* origin, bool handlerMode) @@ -2007,7 +2005,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari } if (controlObject->ctlModel == CONTROL_MODEL_STATUS_ONLY) { - indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } @@ -2034,7 +2032,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* test = getOperParameterTest(value); if (checkValidityOfOriginParameter(origin) == false) { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "SBOw", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_SELECT_FAILED, ctlNum, origin, true); @@ -2141,7 +2139,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari } } else { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; } } else { @@ -2162,12 +2160,12 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari if ((ctlVal == NULL) || (test == NULL) || (ctlNum == NULL) || (origin == NULL) || (check == NULL) || (timeParameter == NULL)) { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; goto free_and_return; } if (checkValidityOfOriginParameter(origin) == false) { - indication = DATA_ACCESS_ERROR_OBJECT_VALUE_INVALID; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, @@ -2223,7 +2221,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari (controlObject->testMode == testCondition) ) == false) { - indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; ControlObject_sendLastApplError(controlObject, connection, "Oper", CONTROL_ERROR_NO_ERROR, ADD_CAUSE_INCONSISTENT_PARAMETERS, @@ -2371,7 +2369,7 @@ Control_writeAccessControlObject(MmsMapping* self, MmsDomain* domain, char* vari MmsValue* origin = getCancelParameterOrigin(value); if ((ctlNum == NULL) || (origin == NULL)) { - indication = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + indication = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; if (DEBUG_IED_SERVER) printf("IED_SERVER: Invalid cancel message!\n"); goto free_and_return; From c621117c5ba7f3fa4d304a68a45200f54b5ef884 Mon Sep 17 00:00:00 2001 From: David Korczynski Date: Mon, 21 Nov 2022 16:35:13 -0800 Subject: [PATCH 008/214] Create cifuzz.yml --- .github/workflows/cifuzz.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/cifuzz.yml diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml new file mode 100644 index 00000000..d52fd077 --- /dev/null +++ b/.github/workflows/cifuzz.yml @@ -0,0 +1,26 @@ +name: CIFuzz +on: [pull_request] +jobs: + Fuzzing: + runs-on: ubuntu-latest + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'libiec61850' + dry-run: false + language: c + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'libiec61850' + fuzz-seconds: 300 + dry-run: false + language: c + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts From 918be7f09b8ab15b2f7f8b801e37ca022b1e6497 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 6 Dec 2022 13:13:23 +0000 Subject: [PATCH 009/214] - fixed - dynamic model helper functions: Check added to Cancel object for CDC APC --- src/iec61850/server/model/cdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index 13967de6..122fa8bd 100644 --- a/src/iec61850/server/model/cdc.c +++ b/src/iec61850/server/model/cdc.c @@ -676,7 +676,7 @@ addAnalogControls(DataObject* parent, uint32_t controlOptions, bool isIntegerNot CAC_AnalogueValue_create("ctlVal", (ModelNode*) cancel, IEC61850_FC_CO, 0, isIntegerNotFloat); - addCommonOperateElements(cancel, isTimeActivated, true); + addCommonOperateElements(cancel, isTimeActivated, false); } } From 81149cd32a6ea996ee7e2a4545c460ce61b8e877 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 26 Nov 2022 09:37:33 +0100 Subject: [PATCH 010/214] - .NET API: Added support for TLS event handler (LIB61850-373) --- dotnet/IEC61850forCSharp/TLS.cs | 576 ++++++++++++++++++++++---------- 1 file changed, 398 insertions(+), 178 deletions(-) diff --git a/dotnet/IEC61850forCSharp/TLS.cs b/dotnet/IEC61850forCSharp/TLS.cs index da7b8d73..05512a9b 100644 --- a/dotnet/IEC61850forCSharp/TLS.cs +++ b/dotnet/IEC61850forCSharp/TLS.cs @@ -1,7 +1,7 @@ /* * TLS.cs * - * Copyright 2017 Michael Zillgith + * Copyright 2017-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,180 +35,400 @@ using IEC61850.Common; /// namespace IEC61850 { - namespace TLS - { - /// - /// A container for TLS configuration and certificates. - /// - public class TLSConfiguration : IDisposable - { - private IntPtr self = IntPtr.Zero; - - private bool allowOnlyKnownCerts = false; - private bool chainValidation = true; - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern IntPtr TLSConfiguration_create(); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_destroy(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setChainValidation (IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - static extern void TLSConfiguration_setClientMode(IntPtr self); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_setOwnKeyFromFile (IntPtr self, string filename, string keyPassword); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen); - - [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.I1)] - static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename); - - public TLSConfiguration() { - self = TLSConfiguration_create (); - } - - ~TLSConfiguration() - { - Dispose (); - } - - internal IntPtr GetNativeInstance() - { - return self; - } - - public bool AllowOnlyKnownCertificates - { - set { - TLSConfiguration_setAllowOnlyKnownCertificates (self, value); - allowOnlyKnownCerts = value; - } - get { - return allowOnlyKnownCerts; - } - } - - public bool ChainValidation - { - set { - TLSConfiguration_setChainValidation (self, value); - chainValidation = value; - } - get { - return chainValidation; - } - } - - public void SetClientMode() - { - TLSConfiguration_setClientMode (self); - } - - public void SetOwnCertificate(string filename) - { - if (TLSConfiguration_setOwnCertificateFromFile (self, filename) == false) { - throw new CryptographicException ("Failed to read certificate from file"); - } - } - - public void SetOwnCertificate(X509Certificate2 cert) - { - byte[] certBytes = cert.GetRawCertData (); - - if (TLSConfiguration_setOwnCertificate (self, certBytes, certBytes.Length) == false) { - throw new CryptographicException ("Failed to set certificate"); - } - } - - public void AddAllowedCertificate(string filename) - { - if (TLSConfiguration_addAllowedCertificateFromFile (self, filename) == false) { - throw new CryptographicException ("Failed to read allowed certificate from file"); - } - } - - public void AddAllowedCertificate(X509Certificate2 cert) - { - byte[] certBytes = cert.GetRawCertData (); - - if (TLSConfiguration_addAllowedCertificate (self, certBytes, certBytes.Length) == false) { - throw new CryptographicException ("Failed to add allowed certificate"); - } - } - - public void AddCACertificate(string filename) - { - if (TLSConfiguration_addCACertificateFromFile (self, filename) == false) { - throw new CryptographicException ("Failed to read CA certificate from file"); - } - } - - public void AddCACertificate(X509Certificate2 cert) - { - byte[] certBytes = cert.GetRawCertData (); - - if (TLSConfiguration_addCACertificate (self, certBytes, certBytes.Length) == false) { - throw new CryptographicException ("Failed to add CA certificate"); - } - } - - public void SetOwnKey (string filename, string password) - { - if (TLSConfiguration_setOwnKeyFromFile (self, filename, password) == false) { - throw new CryptographicException ("Failed to read own key from file"); - } - } - - public void SetOwnKey (X509Certificate2 key, string password) - { - byte[] certBytes = key.Export (X509ContentType.Pkcs12); - - if (TLSConfiguration_setOwnKey (self, certBytes, certBytes.Length, password) == false) { - throw new CryptographicException ("Failed to set own key"); - } - } - - public void Dispose() - { - lock (this) { - if (self != IntPtr.Zero) { - TLSConfiguration_destroy (self); - self = IntPtr.Zero; - } - } - } - - } - } -} \ No newline at end of file + namespace TLS + { + public enum TLSConfigVersion + { + NOT_SELECTED = 0, + SSL_3_0 = 3, + TLS_1_0 = 4, + TLS_1_1 = 5, + TLS_1_2 = 6, + TLS_1_3 = 7 + } + + public enum TLSEventLevel + { + INFO = 0, + WARNING = 1, + INCIDENT = 2 + } + + public enum TLSEventCode + { + ALM_ALGO_NOT_SUPPORTED = 1, + ALM_UNSECURE_COMMUNICATION = 2, + ALM_CERT_UNAVAILABLE = 3, + ALM_BAD_CERT = 4, + ALM_CERT_SIZE_EXCEEDED = 5, + ALM_CERT_VALIDATION_FAILED = 6, + ALM_CERT_REQUIRED = 7, + ALM_HANDSHAKE_FAILED_UNKNOWN_REASON = 8, + WRN_INSECURE_TLS_VERSION = 9, + INF_SESSION_RENEGOTIATION = 10, + ALM_CERT_EXPIRED = 11, + ALM_CERT_REVOKED = 12, + ALM_CERT_NOT_CONFIGURED = 13, + ALM_CERT_NOT_TRUSTED = 14 + } + + public class TLSConnection + { + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern int TLSConnection_getTLSVersion(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr TLSConnection_getPeerAddress(IntPtr self, IntPtr peerAddrBuf); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr TLSConnection_getPeerCertificate(IntPtr self, out int certSize); + + private IntPtr self; + private bool isValid; + + internal TLSConnection(IntPtr self) + { + this.self = self; + isValid = true; + } + + // To be called by event callback caller after callback execution + internal void InValidate() + { + lock (this) + { + isValid = false; + } + } + + /// + /// TLS version used by the connection + /// + public TLSConfigVersion TLSVersion + { + get + { + lock (this) + { + if (isValid) + { + return (TLSConfigVersion)TLSConnection_getTLSVersion((IntPtr)self); + } + else + { + throw new InvalidOperationException("Object cannot be used outside of TLS event callback"); + } + } + } + } + + /// + /// Peer IP address and TCP port of the TLS connection + /// + public string PeerAddress + { + get + { + lock (this) + { + if (isValid) + { + IntPtr peerAddrBuf = Marshal.AllocHGlobal(130); + IntPtr peerAddrStr = TLSConnection_getPeerAddress(this.self, peerAddrBuf); + + string peerAddr = null; + + if (peerAddrStr != IntPtr.Zero) + { + peerAddr = Marshal.PtrToStringAnsi(peerAddrStr); + } + + Marshal.FreeHGlobal(peerAddrBuf); + + return peerAddr; + } + else + { + throw new InvalidOperationException("Object cannot be used outside of TLS event callback"); + } + } + } + } + + /// + /// TLS certificate used by the peer + /// + public byte[] PeerCertificate + { + get + { + lock (this) + { + if (isValid) + { + int certSize; + + IntPtr certBuffer = TLSConnection_getPeerCertificate(self, out certSize); + + if (certBuffer != IntPtr.Zero) + { + if (certSize > 0) + { + byte[] cert = new byte[certSize]; + + Marshal.Copy(certBuffer, cert, 0, certSize); + + return cert; + } + } + + return null; + } + else + { + throw new InvalidOperationException("Object cannot be used outside of TLS event callback"); + } + } + } + } + + } + + /// + /// TLS security event handler + /// + /// user provided context paramter to be passed to the handler + /// severity level of the event + /// code to identify the event type + /// text message describing the event + /// TLS connection that caused the event + public delegate void TLSEventHandler(object parameter, TLSEventLevel eventLevel, TLSEventCode eventCode, string message, TLSConnection connection); + + /// + /// A container for TLS configuration and certificates. + /// + public class TLSConfiguration : IDisposable + { + private IntPtr self = IntPtr.Zero; + + private bool allowOnlyKnownCerts = false; + private bool chainValidation = true; + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern IntPtr TLSConfiguration_create(); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_destroy(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setAllowOnlyKnownCertificates(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setChainValidation(IntPtr self, [MarshalAs(UnmanagedType.I1)] bool value); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setClientMode(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnCertificate(IntPtr self, byte[] certificate, int certLen); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnCertificateFromFile(IntPtr self, string filename); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnKey(IntPtr self, byte[] key, int keyLen, string keyPassword); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_setOwnKeyFromFile(IntPtr self, string filename, string keyPassword); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addAllowedCertificate(IntPtr self, byte[] certificate, int certLen); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addAllowedCertificateFromFile(IntPtr self, string filename); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addCACertificate(IntPtr self, byte[] certificate, int certLen); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool TLSConfiguration_addCACertificateFromFile(IntPtr self, string filename); + + private TLSEventHandler eventHandler = null; + private object eventHandlerParameter = null; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void InternalTLSEventHandler(IntPtr parameter, int eventLevel, int eventCode, IntPtr message, IntPtr tlsCon); + + private InternalTLSEventHandler internalTLSEventHandlerRef = null; + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void TLSConfiguration_setEventHandler(IntPtr self, InternalTLSEventHandler handler, IntPtr parameter); + + void InternalTLSEventHandlerImpl(IntPtr parameter, int eventLevel, int eventCode, IntPtr message, IntPtr tlsCon) + { + if (eventHandler != null) + { + TLSConnection connection = new TLSConnection(tlsCon); + + string msg = Marshal.PtrToStringAnsi(message); + + eventHandler(eventHandlerParameter, (TLSEventLevel)eventLevel, (TLSEventCode)eventCode, msg, connection); + + connection.InValidate(); + } + } + + public void SetEventHandler(TLSEventHandler handler, object parameter) + { + this.eventHandler = handler; + this.eventHandlerParameter = parameter; + + if (internalTLSEventHandlerRef == null) + { + internalTLSEventHandlerRef = new InternalTLSEventHandler(InternalTLSEventHandlerImpl); + + TLSConfiguration_setEventHandler(self, internalTLSEventHandlerRef, IntPtr.Zero); + } + } + + public TLSConfiguration() + { + self = TLSConfiguration_create(); + } + + ~TLSConfiguration() + { + Dispose(); + } + + internal IntPtr GetNativeInstance() + { + return self; + } + + public bool AllowOnlyKnownCertificates + { + set + { + TLSConfiguration_setAllowOnlyKnownCertificates(self, value); + allowOnlyKnownCerts = value; + } + get + { + return allowOnlyKnownCerts; + } + } + + public bool ChainValidation + { + set + { + TLSConfiguration_setChainValidation(self, value); + chainValidation = value; + } + get + { + return chainValidation; + } + } + + public void SetClientMode() + { + TLSConfiguration_setClientMode(self); + } + + public void SetOwnCertificate(string filename) + { + if (TLSConfiguration_setOwnCertificateFromFile(self, filename) == false) + { + throw new CryptographicException("Failed to read certificate from file"); + } + } + + public void SetOwnCertificate(X509Certificate2 cert) + { + byte[] certBytes = cert.GetRawCertData(); + + if (TLSConfiguration_setOwnCertificate(self, certBytes, certBytes.Length) == false) + { + throw new CryptographicException("Failed to set certificate"); + } + } + + public void AddAllowedCertificate(string filename) + { + if (TLSConfiguration_addAllowedCertificateFromFile(self, filename) == false) + { + throw new CryptographicException("Failed to read allowed certificate from file"); + } + } + + public void AddAllowedCertificate(X509Certificate2 cert) + { + byte[] certBytes = cert.GetRawCertData(); + + if (TLSConfiguration_addAllowedCertificate(self, certBytes, certBytes.Length) == false) + { + throw new CryptographicException("Failed to add allowed certificate"); + } + } + + public void AddCACertificate(string filename) + { + if (TLSConfiguration_addCACertificateFromFile(self, filename) == false) + { + throw new CryptographicException("Failed to read CA certificate from file"); + } + } + + public void AddCACertificate(X509Certificate2 cert) + { + byte[] certBytes = cert.GetRawCertData(); + + if (TLSConfiguration_addCACertificate(self, certBytes, certBytes.Length) == false) + { + throw new CryptographicException("Failed to add CA certificate"); + } + } + + public void SetOwnKey(string filename, string password) + { + if (TLSConfiguration_setOwnKeyFromFile(self, filename, password) == false) + { + throw new CryptographicException("Failed to read own key from file"); + } + } + + public void SetOwnKey(X509Certificate2 key, string password) + { + byte[] certBytes = key.Export(X509ContentType.Pkcs12); + + if (TLSConfiguration_setOwnKey(self, certBytes, certBytes.Length, password) == false) + { + throw new CryptographicException("Failed to set own key"); + } + } + + public void Dispose() + { + lock (this) + { + if (self != IntPtr.Zero) + { + TLSConfiguration_destroy(self); + self = IntPtr.Zero; + } + } + } + + } + } +} From 3ad8da23b6379286053dd8e34758a5a0cab15eb9 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 26 Nov 2022 09:38:55 +0100 Subject: [PATCH 011/214] - fixed wrong number in TLS event code define (LIB61850_366) --- hal/inc/tls_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index 9a456752..f4333ca6 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -88,7 +88,7 @@ typedef enum { #define TLS_EVENT_CODE_ALM_CERT_EXPIRED 11 #define TLS_EVENT_CODE_ALM_CERT_REVOKED 12 #define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13 -#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 12 +#define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 14 typedef struct sTLSConnection* TLSConnection; From 6e8053a1ebe80873427052b656af26228f8fa7aa Mon Sep 17 00:00:00 2001 From: Mikael Bourhis Date: Thu, 3 Nov 2022 16:38:35 +0100 Subject: [PATCH 012/214] Examples: add command line parameter for choosing the MMS port in some server examples Some 'server' examples already have this capability. Complete the 'server' example list that support the possibility of using another MMS port than the default one (port '102'). Signed-off-by: Mikael Bourhis --- .../server_example_basic_io/server_example_basic_io.c | 8 +++++++- examples/server_example_control/server_example_control.c | 7 ++++++- .../server_example_deadband/server_example_deadband.c | 8 +++++++- examples/server_example_files/server_example_files.c | 8 +++++++- examples/server_example_logging/server_example_logging.c | 8 +++++++- .../server_example_password_auth.c | 8 +++++++- .../server_example_setting_groups/server_example_sg.c | 8 +++++++- .../server_example_substitution.c | 8 +++++++- .../server_example_threadless/server_example_threadless.c | 7 ++++++- .../server_example_write_handler.c | 8 +++++++- 10 files changed, 68 insertions(+), 10 deletions(-) diff --git a/examples/server_example_basic_io/server_example_basic_io.c b/examples/server_example_basic_io/server_example_basic_io.c index 6023f3b9..3f1ca077 100644 --- a/examples/server_example_basic_io/server_example_basic_io.c +++ b/examples/server_example_basic_io/server_example_basic_io.c @@ -100,6 +100,12 @@ rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conne int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); /* Create new server configuration object */ @@ -163,7 +169,7 @@ main(int argc, char** argv) IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_DC, ACCESS_POLICY_ALLOW); /* MMS server will be instructed to start listening for client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); diff --git a/examples/server_example_control/server_example_control.c b/examples/server_example_control/server_example_control.c index 6159cdda..501acb46 100644 --- a/examples/server_example_control/server_example_control.c +++ b/examples/server_example_control/server_example_control.c @@ -126,6 +126,11 @@ int main(int argc, char** argv) { iedServer = IedServer_create(&iedModel); + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } IedServer_setControlHandler(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1, (ControlHandler) controlHandlerForBinaryOutput, @@ -169,7 +174,7 @@ main(int argc, char** argv) IEDMODEL_GenericIO_GGIO1_SPCSO9); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_deadband/server_example_deadband.c b/examples/server_example_deadband/server_example_deadband.c index 044b72e8..a93236dd 100644 --- a/examples/server_example_deadband/server_example_deadband.c +++ b/examples/server_example_deadband/server_example_deadband.c @@ -36,6 +36,12 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); /* Create new server configuration object */ @@ -74,7 +80,7 @@ main(int argc, char** argv) IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_CF, ACCESS_POLICY_ALLOW); /* MMS server will be instructed to start listening for client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); diff --git a/examples/server_example_files/server_example_files.c b/examples/server_example_files/server_example_files.c index 702eed59..9974fe6f 100644 --- a/examples/server_example_files/server_example_files.c +++ b/examples/server_example_files/server_example_files.c @@ -55,6 +55,12 @@ fileAccessHandler (void* parameter, MmsServerConnection connection, MmsFileServi int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); iedServer = IedServer_create(&iedModel); @@ -70,7 +76,7 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c index 01ad5ae2..51c7a0df 100644 --- a/examples/server_example_logging/server_example_logging.c +++ b/examples/server_example_logging/server_example_logging.c @@ -118,6 +118,12 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); iedServer = IedServer_create(&iedModel); @@ -174,7 +180,7 @@ main(int argc, char** argv) /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_password_auth/server_example_password_auth.c b/examples/server_example_password_auth/server_example_password_auth.c index 2327923f..12938aa1 100644 --- a/examples/server_example_password_auth/server_example_password_auth.c +++ b/examples/server_example_password_auth/server_example_password_auth.c @@ -159,6 +159,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + iedServer = IedServer_create(&iedModel); /* Activate authentication */ @@ -194,7 +200,7 @@ int main(int argc, char** argv) { IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_setting_groups/server_example_sg.c b/examples/server_example_setting_groups/server_example_sg.c index 48f8c4d5..cb3e2ec7 100644 --- a/examples/server_example_setting_groups/server_example_sg.c +++ b/examples/server_example_setting_groups/server_example_sg.c @@ -108,6 +108,12 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + IedServerConfig config = IedServerConfig_create(); //IedServerConfig_enableEditSG(config, false); @@ -132,7 +138,7 @@ main(int argc, char** argv) IedServer_setReadAccessHandler(iedServer, readAccessHandler, NULL); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_substitution/server_example_substitution.c b/examples/server_example_substitution/server_example_substitution.c index c8292dee..e91ff199 100644 --- a/examples/server_example_substitution/server_example_substitution.c +++ b/examples/server_example_substitution/server_example_substitution.c @@ -200,6 +200,12 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + printf("Using libIEC61850 version %s\n", LibIEC61850_getVersionString()); /* Create a new IEC 61850 server instance */ @@ -220,7 +226,7 @@ main(int argc, char** argv) IedServer_handleWriteAccess(iedServer, IEDMODEL_LD1_GGIO1_Ind1_blkEna, writeAccessHandler, NULL); /* MMS server will be instructed to start listening for client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_threadless/server_example_threadless.c b/examples/server_example_threadless/server_example_threadless.c index ae63ce62..13f76927 100644 --- a/examples/server_example_threadless/server_example_threadless.c +++ b/examples/server_example_threadless/server_example_threadless.c @@ -65,6 +65,11 @@ controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* v int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } iedServer = IedServer_create(&iedModel); @@ -86,7 +91,7 @@ main(int argc, char** argv) IEDMODEL_GenericIO_GGIO1_SPCSO4); /* MMS server will be instructed to start listening to client connections. */ - IedServer_startThreadless(iedServer, 102); + IedServer_startThreadless(iedServer, tcpPort); if (!IedServer_isRunning(iedServer)) { printf("Starting server failed! Exit.\n"); diff --git a/examples/server_example_write_handler/server_example_write_handler.c b/examples/server_example_write_handler/server_example_write_handler.c index 49151f06..41e04f4c 100644 --- a/examples/server_example_write_handler/server_example_write_handler.c +++ b/examples/server_example_write_handler/server_example_write_handler.c @@ -41,10 +41,16 @@ writeAccessHandler (DataAttribute* dataAttribute, MmsValue* value, ClientConnect int main(int argc, char** argv) { + int tcpPort = 102; + + if (argc > 1) { + tcpPort = atoi(argv[1]); + } + iedServer = IedServer_create(&iedModel); /* MMS server will be instructed to start listening to client connections. */ - IedServer_start(iedServer, 102); + IedServer_start(iedServer, tcpPort); /* Don't allow access to SP variables by default */ IedServer_setWriteAccessPolicy(iedServer, IEC61850_FC_SP, ACCESS_POLICY_DENY); From 5fe56571579334ba2a0aaf1838008fbe73c436b1 Mon Sep 17 00:00:00 2001 From: Federico Francescon Date: Fri, 9 Dec 2022 16:33:43 +0100 Subject: [PATCH 013/214] Added some options to cmake and updated defautls to match base `make` configuration --- CMakeLists.txt | 4 ++++ config/stack_config.h.cmake | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6172e302..dca3d883 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,9 @@ option(BUILD_PYTHON_BINDINGS "Build Python bindings" OFF) option(CONFIG_MMS_SINGLE_THREADED "Compile for single threaded version" ON) option(CONFIG_MMS_THREADLESS_STACK "Optimize stack for threadless operation (warning: single- or multi-threaded server will not work!)" OFF) +set(CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS 5 CACHE STRING "Configure the maximum number of get file tasks") +set(CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 100 CACHE STRING "Configure the maximum number of dataSet members") + option(CONFIG_ACTIVATE_TCP_KEEPALIVE "Activate TCP keepalive" ON) option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON) @@ -49,6 +52,7 @@ option(CONFIG_IEC61850_SERVICE_TRACKING "Build with support for IEC 61850 servic option(CONFIG_IEC61850_SETTING_GROUPS "Build with support for IEC 61850 setting group services" ON) option(CONFIG_IEC61850_SUPPORT_USER_READ_ACCESS_CONTROL "Allow user provided callback to control read access" ON) option(CONFIG_IEC61850_RCB_ALLOW_ONLY_PRECONFIGURED_CLIENT "allow only configured clients (when pre-configured by ClientLN)" OFF) +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" ) diff --git a/config/stack_config.h.cmake b/config/stack_config.h.cmake index c14b0c20..437d87d9 100644 --- a/config/stack_config.h.cmake +++ b/config/stack_config.h.cmake @@ -146,7 +146,7 @@ #cmakedefine01 CONFIG_IEC61850_SETTING_GROUPS /* default reservation time of a setting group control block in s */ -#define CONFIG_IEC61850_SG_RESVTMS 100 +#cmakedefine CONFIG_IEC61850_SG_RESVTMS @CONFIG_IEC61850_SG_RESVTMS@ /* include support for IEC 61850 log services */ #cmakedefine01 CONFIG_IEC61850_LOG_SERVICE @@ -187,7 +187,10 @@ #define CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS 10 /* Maximum number of the members in a data set (named variable list) */ -#define CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS 50 +#cmakedefine CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS @CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS@ + +/* Maximum number of get file tasks */ +#cmakedefine CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS @CONFIG_MMS_SERVER_MAX_GET_FILE_TASKS@ /* Definition of supported services */ #define MMS_DEFAULT_PROFILE 1 From 9ddb10faea23e2d2ef8772868295d9c892fcd8b7 Mon Sep 17 00:00:00 2001 From: Federico Pellegrin Date: Sun, 11 Dec 2022 05:53:24 +0100 Subject: [PATCH 014/214] tls: add method to reset used CRL (certificate revocation list) Add a method to be able to reset the CRL, otherwise any previously added CRL will stay there until the object is totally destroyed. This proves to be needed for cases when we need to delete the CRL (ie. it expired) during the lifetime of the server. --- hal/inc/tls_config.h | 7 +++++++ hal/tls/mbedtls/tls_mbedtls.c | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index f4333ca6..01e6d524 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -296,6 +296,13 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen); PAL_API bool TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename); +/** + * \brief Removes any CRL (certificate revocation list) currently in use + * + */ +PAL_API void +TLSConfiguration_resetCRL(TLSConfiguration self); + /** * Release all resource allocated by the TLSConfiguration instance * diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index f7d7127e..30a9ac19 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -475,6 +475,14 @@ TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename) return (ret == 0); } +void +TLSConfiguration_resetCRL(TLSConfiguration self) +{ + mbedtls_x509_crl_free(&(self->crl)); + mbedtls_x509_crl_init(&(self->crl)); + self->crlUpdated = Hal_getTimeInMs(); +} + void TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) { From e5c9f2e26760a405e552611e33b00a3888865d7a Mon Sep 17 00:00:00 2001 From: Federico Francescon Date: Mon, 12 Dec 2022 10:37:35 +0100 Subject: [PATCH 015/214] Fixing Security events messages to match IEC62351-100-3 --- hal/inc/tls_config.h | 1 + hal/tls/mbedtls/tls_mbedtls.c | 61 ++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index 01e6d524..8ebaf184 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -89,6 +89,7 @@ typedef enum { #define TLS_EVENT_CODE_ALM_CERT_REVOKED 12 #define TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED 13 #define TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED 14 +#define TLS_EVENT_CODE_ALM_NO_CIPHER 15 typedef struct sTLSConnection* TLSConnection; diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index 30a9ac19..5baa9489 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -174,7 +174,7 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth if (certMatches) *flags = 0; else { - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured", self); + raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: certificate validation: trusted individual certificate not available", self); *flags |= MBEDTLS_X509_BADCERT_OTHER; return 1; @@ -214,7 +214,7 @@ TLSConfiguration_setupComplete(TLSConfiguration self) int ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(self->ownCertificate), &(self->ownKey)); if (ret != 0) { - DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned -0x%x\n", -ret); return false; } } @@ -265,7 +265,7 @@ TLSConfiguration_create() mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED); - mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED); + mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_LEGACY_NO_RENEGOTIATION); /* static int hashes[] = {3,4,5,6,7,8,0}; */ /* mbedtls_ssl_conf_sig_hashes(&(self->conf), hashes); */ @@ -349,7 +349,7 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); return (ret == 0); } @@ -360,7 +360,7 @@ TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* fi int ret = mbedtls_x509_crt_parse_file(&(self->ownCertificate), filename); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse_file returned -0x%x\n", -ret); return (ret == 0); } @@ -371,7 +371,7 @@ TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, cons int ret = mbedtls_pk_parse_key(&(self->ownKey), key, keyLen, (const uint8_t*) keyPassword, (keyPassword == NULL) ? 0 : strlen(keyPassword)); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_pk_parse_key returned -0x%x\n", -ret); return (ret == 0); } @@ -382,7 +382,7 @@ TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, int ret = mbedtls_pk_parse_keyfile(&(self->ownKey), filename, keyPassword); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_pk_parse_keyfile returned -0x%x\n", -ret); return (ret == 0); } @@ -427,7 +427,7 @@ TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certificate, i int ret = mbedtls_x509_crt_parse(&(self->cacerts), certificate, certLen); if (ret != 0) { - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); return false; } @@ -440,7 +440,7 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil int ret = mbedtls_x509_crt_parse_file(&(self->cacerts), filename); if (ret != 0) - DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crt_parse returned -0x%x\n", -ret); return (ret == 0); } @@ -451,7 +451,7 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) int ret = mbedtls_x509_crl_parse(&(self->crl), crl, crlLen); if (ret != 0) { - DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret); } else { self->crlUpdated = Hal_getTimeInMs(); @@ -532,57 +532,66 @@ createSecurityEvents(TLSConfiguration config, int ret, uint32_t flags, TLSSocket return; switch (ret) { + case MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: Algorithm not supported", socket); + break; + + case MBEDTLS_ERR_SSL_NO_CIPHER_CHOSEN: + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_NO_CIPHER, "Alarm: no matching TLS ciphers", socket); + break; case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE: - raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Incident: Algorithm not supported", socket); + raiseSecurityEvent(config, TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED, "Alarm: Algorithm not supported", socket); break; case MBEDTLS_ERR_SSL_BAD_HS_PROTOCOL_VERSION: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Incident: Unsecure communication", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION, "Alarm: Unsecure communication", socket); break; case MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Incident: Certificate unavailable", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_UNAVAILABLE, "Alarm: certificate unavailable", socket); break; case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Incident: Bad certificate", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_BAD_CERT, "Alarm: Bad certificate", socket); break; case MBEDTLS_ERR_SSL_CERTIFICATE_TOO_LARGE: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Incident: TLS certificate size exceeded", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_SIZE_EXCEEDED, "Alarm: TLS certificate size exceeded", socket); break; case MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: certificate validation: certificate signature could not be validated", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: certificate validation: certificate signature could not be validated", socket); break; case MBEDTLS_ERR_SSL_CERTIFICATE_REQUIRED: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Incident: Certificate required", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REQUIRED, "Alarm: Certificate required", socket); break; case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: { if (flags & MBEDTLS_X509_BADCERT_EXPIRED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Incident: Certificate expired", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_EXPIRED, "Alarm: expired certificate", socket); } else if (flags & MBEDTLS_X509_BADCERT_REVOKED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Incident: Certificate revoked", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_REVOKED, "Alarm: revoked certificate", socket); } else if (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Incident: Certificate validation: CA certificate not available", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_TRUSTED, "Alarm: Certificate validation: CA certificate not available", socket); } else if (flags & MBEDTLS_X509_BADCERT_OTHER) { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Incident: Certificate not configured", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Certificate not configured", socket); } - else { - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Incident: Certificate verification failed", socket); + else if (flags & MBEDTLS_X509_BADCERT_BAD_KEY) { + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_NOT_CONFIGURED, "Alarm: Insufficient key length", socket); } + + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_CERT_VALIDATION_FAILED, "Alarm: Certificate verification failed", socket); } break; default: - raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Incident: handshake failed for unknown reason", socket); + raiseSecurityEvent(config,TLS_SEC_EVT_INCIDENT, TLS_EVENT_CODE_ALM_HANDSHAKE_FAILED_UNKNOWN_REASON, "Alarm: handshake failed for unknown reason", socket); break; } } @@ -757,7 +766,7 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClient { if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) { - DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake --> %d\n\n", ret ); + DEBUG_PRINT("TLS", "handshake failed - mbedtls_ssl_handshake returned -0x%x\n", -ret ); uint32_t flags = mbedtls_ssl_get_verify_result(&(self->ssl)); @@ -964,7 +973,7 @@ TLSSocket_close(TLSSocket self) { if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { - DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret); + DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned -0x%x\n", -ret); break; } } From 5a8d3effeca5fccbe449c7635d511c464529b0d2 Mon Sep 17 00:00:00 2001 From: Federico Francescon Date: Mon, 12 Dec 2022 11:39:06 +0100 Subject: [PATCH 016/214] Added check for changed CRL on socket read/write. Added reset of renegotiation ssl cache on CRL add --- hal/tls/mbedtls/tls_mbedtls.c | 102 ++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 43 deletions(-) diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index 5baa9489..91012f0c 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -445,6 +445,26 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil return (ret == 0); } +static void +confCRLUpdated(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) { return; } + + if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) { + + } else { + mbedtls_ssl_cache_entry *cur = self->cache.chain; + + while (cur) { + cur->timestamp = 0; + cur = cur->next; + } + } +} + bool TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) { @@ -454,7 +474,7 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret); } else { - self->crlUpdated = Hal_getTimeInMs(); + confCRLUpdated(self); } return (ret == 0); @@ -469,7 +489,7 @@ TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename) DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret); } else { - self->crlUpdated = Hal_getTimeInMs(); + confCRLUpdated(self); } return (ret == 0); @@ -852,31 +872,46 @@ TLSSocket_performHandshake(TLSSocket self) } } -int -TLSSocket_read(TLSSocket self, uint8_t* buf, int size) +static void +socketCheckCRLUpdated(TLSSocket self) { - if (self->crlUpdated != self->tlsConfig->crlUpdated) { - DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); + if (self->crlUpdated == self->tlsConfig->crlUpdated) { return; } - mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) ); + DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); - self->crlUpdated = self->tlsConfig->crlUpdated; - } + mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) ); - if (self->tlsConfig->renegotiationTimeInMs > 0) { - if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { + self->crlUpdated = self->tlsConfig->crlUpdated; - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self); + /* IEC TS 62351-100-3 Conformance test 6.2.6 requires that upon CRL update a TLS renegotiation should occur */ + self->lastRenegotiationTime = 0; +} - if (TLSSocket_performHandshake(self) == false) { - DEBUG_PRINT("TLS", " renegotiation failed\n"); - return -1; - } - else { - DEBUG_PRINT("TLS", " started renegotiation\n"); - self->lastRenegotiationTime = Hal_getTimeInMs(); - } - } +/* 0 = renegotiation is not needed or it is successfull, -1 = Failed */ +static int +socketCheckRenegotiation(TLSSocket self) +{ + if (self->tlsConfig->renegotiationTimeInMs <= 0) { return 0; } + if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { return 0; } + + 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 -1; + } + + DEBUG_PRINT("TLS", " started renegotiation\n"); + self->lastRenegotiationTime = Hal_getTimeInMs(); + return 0; +} + +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size) +{ + socketCheckCRLUpdated(self); + if (socketCheckRenegotiation(self) != 0) { + return -1; } int ret = mbedtls_ssl_read(&(self->ssl), buf, size); @@ -918,28 +953,9 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size) int ret; int len = size; - if (self->crlUpdated != self->tlsConfig->crlUpdated) { - DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); - - mbedtls_ssl_conf_ca_chain( &(self->conf), &( self->tlsConfig->cacerts), &( self->tlsConfig->crl) ); - - self->crlUpdated = self->tlsConfig->crlUpdated; - } - - if (self->tlsConfig->renegotiationTimeInMs > 0) { - if (Hal_getTimeInMs() > self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { - - raiseSecurityEvent(self->tlsConfig, TLS_SEC_EVT_INFO, TLS_EVENT_CODE_INF_SESSION_RENEGOTIATION, "Info: session renegotiation started", self); - - if (TLSSocket_performHandshake(self) == false) { - DEBUG_PRINT("TLS", " renegotiation failed\n"); - return -1; - } - else { - DEBUG_PRINT("TLS", " started renegotiation\n"); - self->lastRenegotiationTime = Hal_getTimeInMs(); - } - } + socketCheckCRLUpdated(self); + if (socketCheckRenegotiation(self) != 0) { + return -1; } while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0) From 21903710adc9a4faca79e5f1aa0cfd3f4ae0e69e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 13 Dec 2022 14:07:01 +0000 Subject: [PATCH 017/214] - applied coding style --- hal/inc/tls_config.h | 1 - hal/tls/mbedtls/tls_mbedtls.c | 47 ++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index 8ebaf184..39aa691f 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -299,7 +299,6 @@ TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename); /** * \brief Removes any CRL (certificate revocation list) currently in use - * */ PAL_API void TLSConfiguration_resetCRL(TLSConfiguration self); diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index 91012f0c..df7b5d38 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -446,16 +446,16 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil } static void -confCRLUpdated(TLSConfiguration self) +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) { return; } - - if (self->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) { + if (self->useSessionResumption == false) + return; - } else { + if (self->conf.endpoint == MBEDTLS_SSL_IS_SERVER) + { mbedtls_ssl_cache_entry *cur = self->cache.chain; while (cur) { @@ -474,7 +474,7 @@ TLSConfiguration_addCRL(TLSConfiguration self, uint8_t* crl, int crlLen) DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse returned -0x%x\n", -ret); } else { - confCRLUpdated(self); + udpatedCRL(self); } return (ret == 0); @@ -489,7 +489,7 @@ TLSConfiguration_addCRLFromFile(TLSConfiguration self, const char* filename) DEBUG_PRINT("TLS", "mbedtls_x509_crl_parse_file returned %d\n", ret); } else { - confCRLUpdated(self); + udpatedCRL(self); } return (ret == 0); @@ -873,9 +873,10 @@ TLSSocket_performHandshake(TLSSocket self) } static void -socketCheckCRLUpdated(TLSSocket self) +checkForCRLUpdate(TLSSocket self) { - if (self->crlUpdated == self->tlsConfig->crlUpdated) { return; } + if (self->crlUpdated == self->tlsConfig->crlUpdated) + return; DEBUG_PRINT("TLS", "CRL updated -> refresh CA chain\n"); @@ -887,30 +888,35 @@ socketCheckCRLUpdated(TLSSocket self) self->lastRenegotiationTime = 0; } -/* 0 = renegotiation is not needed or it is successfull, -1 = Failed */ -static int -socketCheckRenegotiation(TLSSocket self) +/* true = renegotiation is not needed or it is successfull, false = Failed */ +static bool +startRenegotiationIfRequired(TLSSocket self) { - if (self->tlsConfig->renegotiationTimeInMs <= 0) { return 0; } - if (Hal_getTimeInMs() <= self->lastRenegotiationTime + self->tlsConfig->renegotiationTimeInMs) { return 0; } + 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 -1; + return false; } DEBUG_PRINT("TLS", " started renegotiation\n"); self->lastRenegotiationTime = Hal_getTimeInMs(); - return 0; + + return true; } int TLSSocket_read(TLSSocket self, uint8_t* buf, int size) { - socketCheckCRLUpdated(self); - if (socketCheckRenegotiation(self) != 0) { + checkForCRLUpdate(self); + + if (startRenegotiationIfRequired(self) == false) { return -1; } @@ -953,8 +959,9 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size) int ret; int len = size; - socketCheckCRLUpdated(self); - if (socketCheckRenegotiation(self) != 0) { + checkForCRLUpdate(self); + + if (startRenegotiationIfRequired(self) == false) { return -1; } From 97923e72388f43fa4f759025445b14539a5916c6 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 19 Dec 2022 12:04:01 +0000 Subject: [PATCH 018/214] - updated required mbedtls version to 2.28.x --- CMakeLists.txt | 10 +++++----- Makefile | 4 ++-- README.md | 6 +++--- hal/CMakeLists.txt | 4 ++-- third_party/mbedtls/README | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dca3d883..28477596 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,8 @@ option(CONFIG_INCLUDE_GOOSE_SUPPORT "Build with GOOSE support" ON) option(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB "Build with pre-compiled mbedtls dynamic library" OFF) -set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" ) -set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include" CACHE STRING "Path to search for the mbedtls include files" ) +set(CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/library" CACHE STRING "Path to search for the mbedtls dynamic libraries" ) +set(CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include" CACHE STRING "Path to search for the mbedtls include files" ) # choose the library features which shall be included option(CONFIG_IEC61850_CONTROL_SERVICE "Build with support for IEC 61850 control features" ON) @@ -130,10 +130,10 @@ set(USE_PREBUILD_MBEDTLS 1) set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) -if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16) +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) set(WITH_MBEDTLS 1) -set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16/include") -endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.16) +set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include") +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) if(WITH_MBEDTLS) diff --git a/Makefile b/Makefile index db06c7f8..ef530346 100644 --- a/Makefile +++ b/Makefile @@ -78,9 +78,9 @@ LIB_INCLUDE_DIRS += third_party/winpcap/Include endif ifdef WITH_MBEDTLS -LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.16/library +LIB_SOURCE_DIRS += third_party/mbedtls/mbedtls-2.28/library LIB_SOURCE_DIRS += hal/tls/mbedtls -LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.16/include +LIB_INCLUDE_DIRS += third_party/mbedtls/mbedtls-2.28/include LIB_INCLUDE_DIRS += hal/tls/mbedtls CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"' CFLAGS += -D'CONFIG_MMS_SUPPORT_TLS=1' diff --git a/README.md b/README.md index e423d312..66a2734c 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,9 @@ You can test the server examples by using a generic client or the provided clien ## Building the library with TLS support -Download, unpack, and copy mbedtls-2.16 into the third_party/mbedtls folder. +Download, unpack, and copy mbedtls-2.28 into the third_party/mbedtls folder. -NOTE: The current version support mbedtls version 2.16. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.16". +NOTE: The current version support mbedtls version 2.28. When you download the source archive from https://tls.mbed.org/ you have to rename the extracted folder to "mbedtls-2.28". In the main libiec61850 folder run @@ -96,7 +96,7 @@ In the main libiec61850 folder run make WITH_MBEDTLS=1 ``` -When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.16 is present. +When using CMake the library is built automatically with TLS support when the folder third_party/mbedtls/mbedtls-2.28 is present. ## Installing the library and the API headers diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt index 33975c28..9b421f6b 100644 --- a/hal/CMakeLists.txt +++ b/hal/CMakeLists.txt @@ -10,7 +10,7 @@ endif() project(hal) set(LIBHAL_VERSION_MAJOR "2") -set(LIBHAL_VERSION_MINOR "0") +set(LIBHAL_VERSION_MINOR "1") set(LIBHAL_VERSION_PATCH "0") # feature checks @@ -129,7 +129,7 @@ include_directories( if(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) link_directories(${CONFIG_EXTERNAL_MBEDTLS_DYNLIB_PATH}) else() -file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.16/library/*.c) +file(GLOB tls_SRCS ${CMAKE_CURRENT_LIST_DIR}/../third_party/mbedtls/mbedtls-2.28/library/*.c) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) add_definitions(-DMBEDTLS_CONFIG_FILE="mbedtls_config.h") diff --git a/third_party/mbedtls/README b/third_party/mbedtls/README index 04597eb4..b6171f07 100644 --- a/third_party/mbedtls/README +++ b/third_party/mbedtls/README @@ -1,9 +1,9 @@ README ------ -For TLS support with mbedtls download the source tarball of version 2.16.x and extract here in the subfolder +For TLS support with mbedtls download the source tarball of version 2.28.x and extract here in the subfolder -mbedtls-2.16 +mbedtls-2.28 -After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.16". Otherwise the build system will not find the library. +After extracting of the archive you may have to rename the folder to match the exact name "mbedtls-2.28". Otherwise the build system will not find the library. From eba36fec65c5793c6ddcb048959d1a31fe330c18 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 19 Dec 2022 12:10:32 +0000 Subject: [PATCH 019/214] - renamed TLSConfiguration_EventLevel to TLSEventLevel --- examples/tls_client_example/tls_client_example.c | 2 +- examples/tls_server_example/tls_server_example.c | 2 +- hal/inc/tls_config.h | 4 ++-- hal/tls/mbedtls/tls_mbedtls.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/tls_client_example/tls_client_example.c b/examples/tls_client_example/tls_client_example.c index 5dd982d2..db730f3f 100644 --- a/examples/tls_client_example/tls_client_example.c +++ b/examples/tls_client_example/tls_client_example.c @@ -30,7 +30,7 @@ reportCallbackFunction(void* parameter, ClientReport report) } static void -securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) +securityEventHandler(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) { (void)parameter; diff --git a/examples/tls_server_example/tls_server_example.c b/examples/tls_server_example/tls_server_example.c index e2324faf..6db25222 100644 --- a/examples/tls_server_example/tls_server_example.c +++ b/examples/tls_server_example/tls_server_example.c @@ -103,7 +103,7 @@ clientAuthenticator(void* parameter, AcseAuthenticationParameter authParameter, } static void -securityEventHandler(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) +securityEventHandler(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* msg, TLSConnection con) { (void)parameter; diff --git a/hal/inc/tls_config.h b/hal/inc/tls_config.h index 39aa691f..2896a108 100644 --- a/hal/inc/tls_config.h +++ b/hal/inc/tls_config.h @@ -73,7 +73,7 @@ typedef enum { TLS_SEC_EVT_INFO = 0, TLS_SEC_EVT_WARNING = 1, TLS_SEC_EVT_INCIDENT = 2 -} TLSConfiguration_EventLevel; +} TLSEventLevel; #define TLS_EVENT_CODE_ALM_ALGO_NOT_SUPPORTED 1 #define TLS_EVENT_CODE_ALM_UNSECURE_COMMUNICATION 2 @@ -125,7 +125,7 @@ TLSConnection_getPeerCertificate(TLSConnection self, int* certSize); PAL_API TLSConfigVersion TLSConnection_getTLSVersion(TLSConnection self); -typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSConfiguration_EventLevel eventLevel, int eventCode, const char* message, TLSConnection con); +typedef void (*TLSConfiguration_EventHandler)(void* parameter, TLSEventLevel eventLevel, int eventCode, const char* message, TLSConnection con); /** * \brief Set the security event handler diff --git a/hal/tls/mbedtls/tls_mbedtls.c b/hal/tls/mbedtls/tls_mbedtls.c index df7b5d38..e2f152d0 100644 --- a/hal/tls/mbedtls/tls_mbedtls.c +++ b/hal/tls/mbedtls/tls_mbedtls.c @@ -105,7 +105,7 @@ struct sTLSSocket { }; static void -raiseSecurityEvent(TLSConfiguration config, TLSConfiguration_EventLevel eventCategory, int eventCode, const char* message, TLSSocket socket) +raiseSecurityEvent(TLSConfiguration config, TLSEventLevel eventCategory, int eventCode, const char* message, TLSSocket socket) { if (config->eventHandler) { config->eventHandler(config->eventHandlerParameter, eventCategory, eventCode, message, (TLSConnection)socket); From 9335afcca53a29c7b8b85d318a99ac7c48f78677 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 2 Jan 2023 15:52:17 +0000 Subject: [PATCH 020/214] - fixed - outstanding call not released in IedConnection_getDataSetDirectoryAsync (LIB61850-379) --- src/iec61850/client/ied_connection.c | 31 +++++++++++++++++++--------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 9ab48163..b7395c7b 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -3294,8 +3294,6 @@ uint32_t IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, IedConnection_GenericServiceHandler handler, void* parameter) { - uint32_t invokeId = 0; - char domainIdBuffer[65]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; @@ -3338,7 +3336,7 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons isAssociationSpecific = true; } - MmsError mmsError; + MmsError mmsError = MMS_ERROR_NONE; IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); @@ -3379,8 +3377,6 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons &mmsError, domainId, itemId, dataSetEntries, createDataSetAsyncHandler, self); } - invokeId = call->invokeId; - *error = iedConnection_mapMmsErrorToIedError(mmsError); cleanup_list: @@ -3388,7 +3384,15 @@ cleanup_list: LinkedList_destroyDeep(dataSetEntries, (LinkedListValueDeleteFunction) MmsVariableAccessSpecification_destroy); exit_function: - return invokeId; + + if (mmsError != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + + return 0; + } + else { + return call->invokeId; + } } LinkedList /* */ @@ -3513,6 +3517,8 @@ getDataSetDirectoryAsyncHandler(uint32_t invokeId, void* parameter, MmsError mms if (handler) handler(call->invokeId, call->callbackParameter, err, dataSetMembers, deletable); + + iedConnection_releaseOutstandingCall(self, call); } if (specs) @@ -3523,8 +3529,6 @@ uint32_t IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference, IedConnection_GetDataSetDirectoryHandler handler, void* parameter) { - uint32_t invokeId = 0; - IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { @@ -3580,7 +3584,6 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error MmsError mmsError = MMS_ERROR_NONE; - if (isAssociationSpecific) MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self); @@ -3590,7 +3593,15 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error *error = iedConnection_mapMmsErrorToIedError(mmsError); exit_function: - return invokeId; + + if (mmsError != MMS_ERROR_NONE) { + iedConnection_releaseOutstandingCall(self, call); + + return 0; + } + else { + return call->invokeId; + } } ClientDataSet From 398d06684a4df4615957e136f037924b401e88a1 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 3 Jan 2023 12:20:08 +0000 Subject: [PATCH 021/214] - updated error handling in IedConnection_getDataSetDirectoryAsync/IedConnection_createDataSetAsync (LIB61850-379) --- src/iec61850/client/ied_connection.c | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index b7395c7b..807037be 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -3294,6 +3294,19 @@ uint32_t IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, const char* dataSetReference, LinkedList /* char* */ dataSetElements, IedConnection_GenericServiceHandler handler, void* parameter) { + MmsError mmsError = MMS_ERROR_NONE; + + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); + + if (call == NULL) { + *error = IED_ERROR_OUTSTANDING_CALL_LIMIT_REACHED; + goto exit_function; + } + + call->callback = handler; + call->callbackParameter = parameter; + call->invokeId = 0; + char domainIdBuffer[65]; char itemIdBuffer[DATA_SET_MAX_NAME_LENGTH + 1]; @@ -3336,19 +3349,6 @@ IedConnection_createDataSetAsync(IedConnection self, IedClientError* error, cons isAssociationSpecific = true; } - 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; - LinkedList dataSetEntries = LinkedList_create(); LinkedList dataSetElement = LinkedList_getNext(dataSetElements); @@ -3385,7 +3385,7 @@ cleanup_list: exit_function: - if (mmsError != MMS_ERROR_NONE) { + if (*error != IED_ERROR_OK) { iedConnection_releaseOutstandingCall(self, call); return 0; @@ -3529,6 +3529,8 @@ uint32_t IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error, const char* dataSetReference, IedConnection_GetDataSetDirectoryHandler handler, void* parameter) { + MmsError mmsError = MMS_ERROR_NONE; + IedConnectionOutstandingCall call = iedConnection_allocateOutstandingCall(self); if (call == NULL) { @@ -3582,8 +3584,6 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error isAssociationSpecific = true; } - MmsError mmsError = MMS_ERROR_NONE; - if (isAssociationSpecific) MmsConnection_readNamedVariableListDirectoryAssociationSpecificAsync(self->connection, &(call->invokeId), &mmsError, itemId, getDataSetDirectoryAsyncHandler, self); @@ -3594,7 +3594,7 @@ IedConnection_getDataSetDirectoryAsync(IedConnection self, IedClientError* error exit_function: - if (mmsError != MMS_ERROR_NONE) { + if (*error != IED_ERROR_OK) { iedConnection_releaseOutstandingCall(self, call); return 0; From 6895d8214b7e13d29b10a36778674f5a86176672 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 20 Jan 2023 16:02:19 +0000 Subject: [PATCH 022/214] - MMS server: add function to ignore client requests (for test purposes) --- hal/memory/lib_memory.c | 3 --- src/iec61850/inc/iec61850_server.h | 10 ++++++- src/iec61850/server/impl/ied_server.c | 10 ++++++- src/mms/asn1/asn1_ber_primitive_value.c | 26 +++++++++---------- src/mms/asn1/ber_decode.c | 2 +- src/mms/asn1/ber_encoder.c | 25 ++++++++++++++++-- src/mms/asn1/ber_integer.c | 2 +- src/mms/inc/mms_server.h | 5 +++- src/mms/inc_private/mms_server_internal.h | 3 ++- src/mms/inc_private/mms_server_libinternal.h | 4 +-- src/mms/iso_mms/server/mms_server.c | 12 ++++++--- .../iso_mms/server/mms_server_connection.c | 3 +++ 12 files changed, 75 insertions(+), 30 deletions(-) diff --git a/hal/memory/lib_memory.c b/hal/memory/lib_memory.c index adeb105a..74fde5b4 100644 --- a/hal/memory/lib_memory.c +++ b/hal/memory/lib_memory.c @@ -38,7 +38,6 @@ Memory_malloc(size_t size) return memory; } - void* Memory_calloc(size_t nmemb, size_t size) { @@ -50,7 +49,6 @@ Memory_calloc(size_t nmemb, size_t size) return memory; } - void * Memory_realloc(void *ptr, size_t size) { @@ -67,4 +65,3 @@ Memory_free(void* memb) { free(memb); } - diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index d206b904..e3790011 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -3,7 +3,7 @@ * * IEC 61850 server API for libiec61850. * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -783,6 +783,14 @@ typedef void (*IedConnectionIndicationHandler) (IedServer self, ClientConnection LIB61850_API void IedServer_setConnectionIndicationHandler(IedServer self, IedConnectionIndicationHandler handler, void* parameter); +/** + * \brief Ignore all requests from clients + * + * \param self the instance of IedServer to configure. + * \param enable when true all requests from clients will be ignored + */ +void +IedServer_ignoreClientRequests(IedServer self, bool enable); /**@}*/ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 9416d200..c543a41d 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1,7 +1,7 @@ /* * ied_server.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -1909,3 +1909,11 @@ IedServer_setTimeQuality(IedServer self, bool leapSecondKnown, bool clockFailure self->timeQuality = timeQuality; } + +void +IedServer_ignoreClientRequests(IedServer self, bool enable) +{ + if (self->mmsServer) { + MmsServer_ignoreClientRequests(self->mmsServer, enable); + } +} diff --git a/src/mms/asn1/asn1_ber_primitive_value.c b/src/mms/asn1/asn1_ber_primitive_value.c index 0dbd15bf..55cf626b 100644 --- a/src/mms/asn1/asn1_ber_primitive_value.c +++ b/src/mms/asn1/asn1_ber_primitive_value.c @@ -1,24 +1,24 @@ /* * asn1_ber_primitive_value.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * - * This file is part of libIEC61850. + * This file is part of libIEC61850. * - * libIEC61850 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * libIEC61850 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . * - * See COPYING file for the complete license text. + * See COPYING file for the complete license text. */ #include "libiec61850_platform_includes.h" diff --git a/src/mms/asn1/ber_decode.c b/src/mms/asn1/ber_decode.c index 12078610..8a4866a8 100644 --- a/src/mms/asn1/ber_decode.c +++ b/src/mms/asn1/ber_decode.c @@ -1,7 +1,7 @@ /* * ber_decoder.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/mms/asn1/ber_encoder.c b/src/mms/asn1/ber_encoder.c index 63566f38..53a5d178 100644 --- a/src/mms/asn1/ber_encoder.c +++ b/src/mms/asn1/ber_encoder.c @@ -1,7 +1,7 @@ /* * ber_encoder.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * @@ -356,6 +356,28 @@ BerEncoder_UInt32determineEncodedSize(uint32_t value) return size; } +int +BerEncoder_Int32determineEncodedSize(int32_t value) +{ + uint8_t* valueArray = (uint8_t*) &value; + uint8_t valueBuffer[5]; + + valueBuffer[0] = 0; + + int i; + for (i = 0; i < 4; i++) { + valueBuffer[i + 1] = valueArray[i]; + } + +#if (ORDER_LITTLE_ENDIAN == 1) + BerEncoder_revertByteOrder(valueBuffer + 1, 4); +#endif + + int size = BerEncoder_compressInteger(valueBuffer, 5); + + return size; +} + int BerEncoder_determineLengthSize(uint32_t length) { @@ -457,7 +479,6 @@ BerEncoder_encodeOIDToBuffer(const char* oidString, uint8_t* buffer, int maxBufL requiredBytes--; } } - } return encodedBytes; diff --git a/src/mms/asn1/ber_integer.c b/src/mms/asn1/ber_integer.c index 9a7e8030..01281e3f 100644 --- a/src/mms/asn1/ber_integer.c +++ b/src/mms/asn1/ber_integer.c @@ -1,7 +1,7 @@ /* * ber_integer.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2022 Michael Zillgith * * This file is part of libIEC61850. * diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 7e6118d0..98e4c796 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -1,7 +1,7 @@ /* * mms_server.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -370,6 +370,9 @@ MmsServerConnection_getLocalAddress(MmsServerConnection self); LIB61850_INTERNAL void* MmsServerConnection_getSecurityToken(MmsServerConnection self); +LIB61850_INTERNAL void +MmsServer_ignoreClientRequests(MmsServer self, bool enable);; + /**@}*/ #ifdef __cplusplus diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 780e1d74..727e97a3 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -135,7 +135,8 @@ struct sMmsServer { Map openConnections; Map valueCaches; - bool isLocked; + + bool blockRequests; ByteBuffer* transmitBuffer; /* global buffer for encoding reports, delayed responses... */ #if (CONFIG_MMS_THREADLESS_STACK != 1) diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index ac16e445..444989c9 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -1,7 +1,7 @@ /* * mms_server_libinternal.h * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -196,7 +196,7 @@ MmsServer_getConnectionCounter(MmsServer self); LIB61850_INTERNAL void MmsServer_stopListeningThreadless(MmsServer self); -LIB61850_INTERNAL const char* +LIB61850_INTERNAL const char* MmsServer_getFilesystemBasepath(MmsServer self); #endif /* MMS_SERVER_LIBINTERNAL_H_ */ diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index 3905b52d..ed2cf725 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -1,7 +1,7 @@ /* * mms_server.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -95,13 +95,13 @@ MmsServer_create(MmsDevice* device, TLSConfiguration tlsConfiguration) if (self->valueCaches == NULL) goto exit_error; - self->isLocked = false; - self->transmitBuffer = ByteBuffer_create(NULL, CONFIG_MMS_MAXIMUM_PDU_SIZE); if (self->transmitBuffer == NULL) goto exit_error; + self->blockRequests = false; + #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) self->fileServiceEnabled = true; self->dynamicVariableListServiceEnabled = true; @@ -820,4 +820,8 @@ MmsServer_getFilesystemBasepath(MmsServer self) #endif } - +void +MmsServer_ignoreClientRequests(MmsServer self, bool enable) +{ + self->blockRequests = enable; +} diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index b14ecc3a..4f914081 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -665,6 +665,9 @@ handleConfirmedResponsePdu( static inline void MmsServerConnection_parseMessage(MmsServerConnection self, ByteBuffer* message, ByteBuffer* response) { + if (self->server->blockRequests) + return; + uint8_t* buffer = message->buffer; if (message->size < 2) From 11f997270a495a76452d5b290014ab8a2fc7aaad Mon Sep 17 00:00:00 2001 From: Mikael Bourhis Date: Wed, 18 Jan 2023 17:10:21 +0100 Subject: [PATCH 023/214] Python wrapper: add RCB subscription example Signed-off-by: Mikael Bourhis --- pyiec61850/examples/rcbSubscriptionExample.py | 260 ++++++++++++++++++ 1 file changed, 260 insertions(+) create mode 100644 pyiec61850/examples/rcbSubscriptionExample.py diff --git a/pyiec61850/examples/rcbSubscriptionExample.py b/pyiec61850/examples/rcbSubscriptionExample.py new file mode 100644 index 00000000..d4d05aa5 --- /dev/null +++ b/pyiec61850/examples/rcbSubscriptionExample.py @@ -0,0 +1,260 @@ +#!/usr/bin/python + +''' +Example of RCB subscription, with the Python wrapper + +This example is intended to be used with server_example_basic_io. + +Usage: + 'sudo python3 ./pyiec61850/examples/rcbSubscriptionExample.py' + or + 'sudo python3 ./pyiec61850/examples/rcbSubscriptionExample.py localhost 102' + or + 'python3 ./pyiec61850/examples/rcbSubscriptionExample.py localhost 8102' + + +Swig generates 2 wrapped objects: + - a generic RCB handler + - a generic RCB subscriber + +The user needs to: + - create his specific RCB handler in Python, + for processing the received RCB as he wants + - create his specific RCB subscriber in Python, + with his registering parameters + - connect his handler to his subscriber with a composition relationship +''' + +import time +import sys +import iec61850 + + +def open_connection(ip_address, mms_port): + '''Open the connection with the IED''' + l_connection = iec61850.IedConnection_create() + l_error = iec61850.IedConnection_connect(l_connection, ip_address, mms_port) + return l_error, l_connection + + +def close_connection(i_connection): + '''Close the connection with the IED''' + iec61850.IedConnection_close(i_connection) + + +def destroy_connection(i_connection): + '''Destroy (free) the Connection object''' + iec61850.IedConnection_destroy(i_connection) + + +class PyRCBHandler(iec61850.RCBHandler): + '''Class processing the received RCB''' + + def __init__(self): + iec61850.RCBHandler.__init__(self) + + def trigger(self): + '''Method for triggering the handler and processing the last received RCB. + In these example, we only print some attributs and data of the RCB''' + + # the following section is the application part of the Swig C subthread: + # we must catch the Python exceptions, otherwise it will crash. + try: + l_rcb_ref = iec61850.ClientReport_getRcbReference(self._client_report) + print(f"\nNew received RCB: {l_rcb_ref}") + + if iec61850.ClientReport_hasDataSetName(self._client_report): + l_dataset_name = iec61850.ClientReport_getDataSetName(self._client_report) + print(f"\tDataSet name: {l_dataset_name}") + + print(f"\tReport id: {iec61850.ClientReport_getRptId(self._client_report)}") + + if iec61850.ClientReport_hasSeqNum(self._client_report): + l_seq_num = iec61850.ClientReport_getSeqNum(self._client_report) + print(f"\tSequence num: {l_seq_num}") + + if iec61850.ClientReport_hasSubSeqNum(self._client_report): + l_sub_seq_num = iec61850.ClientReport_getSubSeqNum(self._client_report) + print(f"\tSub-sequence num: {l_sub_seq_num}") + + if iec61850.ClientReport_hasTimestamp(self._client_report): + l_timestamp_millisec =iec61850.ClientReport_getTimestamp(self._client_report) + print(f"\tTimestamp in millsec: {l_timestamp_millisec}") + + mms_value_array = iec61850.ClientReport_getDataSetValues(self._client_report) + mms_value_array_size = iec61850.MmsValue_getArraySize(mms_value_array) + print(f"\tDataSet size: {mms_value_array_size}") + + for mms_value_index in range(mms_value_array_size): + mms_value = iec61850.MmsValue_getElement(mms_value_array, mms_value_index) + mms_value_type = iec61850.MmsValue_getTypeString(mms_value) + + if mms_value_type == "boolean": + print(f"\tMMS value: {iec61850.MmsValue_getBoolean(mms_value)}") + elif mms_value_type == "float": + print(f"\tMMS value: {iec61850.MmsValue_toFloat(mms_value)}") + else: + print("\tMMS value: other type") + + l_reason = iec61850.ClientReport_getReasonForInclusion(self._client_report, + mms_value_index) + l_reason_str = iec61850.ReasonForInclusion_getValueAsString(l_reason) + print(f"\tReason for inclusion: {l_reason_str}") + + except RuntimeError as l_error: + print(f"Runtime Error (in subscriber thread): {l_error}") + except AssertionError as l_error: + print(f"Assertion Error (in subscriber thread): {l_error}") + except Exception as l_exception: + print(f"Exception (in subscriber thread): {l_exception}") + + +class PyRCBSubscriber: + '''Class representing a RCB subscriber in Python, + and that owns the RCB handler in Python for processing the received RCB''' + + def __init__(self): + self._libiec61850_rcb_client = None + self._internal_rcb_handler = PyRCBHandler() + self._wrapped_rcb_subscriber = iec61850.RCBSubscriber() # generic RCB subscriber + self._connection = None # do not destroy it + self._libiec61850_error_code = iec61850.IED_ERROR_OK + + def __del__(self): + if self._connection is not None: + self.destroy() + + + def subscribe(self, i_connection, i_report_control_block_ref): + '''Select the subscription parameters and create the RCB subscription''' + + # preconditions + assert iec61850.IedConnection_getState(i_connection) == iec61850.IED_STATE_CONNECTED, \ + "error: Not connected" + assert i_report_control_block_ref, "error: the reference of the ReportControlBlock is empty" + assert self._libiec61850_rcb_client is None, "error: the RCB client is already created" + + self._connection = i_connection + + # Like the usual RCB subscription with the C API: + # read data set + print(f"RCBSubscription: create subscription for: '{i_report_control_block_ref}'") + l_return_value = iec61850.pyWrap_IedConnection_getRCBValues(self._connection, + i_report_control_block_ref, + None) + if isinstance(l_return_value, int): + self._libiec61850_error_code = l_return_value + else: + [self._libiec61850_rcb_client, self._libiec61850_error_code] = l_return_value + + if self._libiec61850_error_code != iec61850.IED_ERROR_OK: + raise RuntimeError('IEC61850 error') + + + # Specific instructions with Python: + # Initialize the generic and wrapped 'subscriber' + l_client_report_control_block_id = \ + iec61850.ClientReportControlBlock_getRptId(self._libiec61850_rcb_client) + self._wrapped_rcb_subscriber.setIedConnection(self._connection) + self._wrapped_rcb_subscriber.setRcbReference(i_report_control_block_ref) + self._wrapped_rcb_subscriber.setRcbRptId(l_client_report_control_block_id) + + # Specific instructions with Python: + # Connect the specific callback/handler + self._internal_rcb_handler.thisown = 0 # the following subscriber will be the owner of this handler + self._wrapped_rcb_subscriber.setEventHandler(self._internal_rcb_handler) + + + # Like the usual RCB subscription, same feature but with a specific Python object + # Install handler for reports + l_registering_status = self._wrapped_rcb_subscriber.subscribe() + assert l_registering_status is True, "Error: Failed to register the RCBSubscriber" + + # Like the usual RCB subscription with the C API: + # Set trigger options and enable report + l_trigger_options = iec61850.TRG_OPT_DATA_UPDATE | \ + iec61850.TRG_OPT_INTEGRITY | \ + iec61850.TRG_OPT_GI | \ + iec61850.TRG_OPT_DATA_CHANGED | \ + iec61850.TRG_OPT_QUALITY_CHANGED + l_rcb_attributes = iec61850.RCB_ELEMENT_RPT_ENA | iec61850.RCB_ELEMENT_TRG_OPS + + iec61850.ClientReportControlBlock_setTrgOps(self._libiec61850_rcb_client, + l_trigger_options) + + iec61850.ClientReportControlBlock_setRptEna(self._libiec61850_rcb_client, True) + + self._libiec61850_error_code = \ + iec61850.pyWrap_IedConnection_setRCBValues(self._connection, + self._libiec61850_rcb_client, + l_rcb_attributes, + True) + + # Check subscription status + if self._libiec61850_error_code != iec61850.IED_ERROR_OK: + raise RuntimeError('IEC61850 error') + + + def destroy(self): + '''Stop the RCB subscription and destroy the internal objects''' + + if self._libiec61850_rcb_client and \ + iec61850.IedConnection_getState(self._connection) == iec61850.IED_STATE_CONNECTED: + # Disable reporting + iec61850.ClientReportControlBlock_setRptEna(self._libiec61850_rcb_client, False) + self._libiec61850_error_code = \ + iec61850.pyWrap_IedConnection_setRCBValues(self._connection, + self._libiec61850_rcb_client, + iec61850.RCB_ELEMENT_RPT_ENA, + True) + # Check the 'disable reporting' command result + if self._libiec61850_error_code != iec61850.IED_ERROR_OK: + raise RuntimeError('IEC61850 error') + + # Destroy the libiec61850 objects + if self._libiec61850_rcb_client: + iec61850.ClientReportControlBlock_destroy(self._libiec61850_rcb_client) + self._libiec61850_rcb_client = None + + # Destroy the RCB subscriber + if self._wrapped_rcb_subscriber: + del self._wrapped_rcb_subscriber + self._wrapped_rcb_subscriber = None + + +# MAIN +L_RCBREF_STATUS = "simpleIOGenericIO/LLN0.RP.EventsRCB01" +L_RCBREF_MEASUREMENTS = "simpleIOGenericIO/LLN0.BR.Measurements01" + +HOSTNAME = "localhost" +PORT = 102 +if len(sys.argv) > 1: + HOSTNAME = sys.argv[1] +if len(sys.argv) > 2: + PORT = int(sys.argv[2]) + +[error, con] = open_connection(HOSTNAME, PORT) + +if error == iec61850.IED_ERROR_OK: + try: + rcb_subscriber_1 = PyRCBSubscriber() + rcb_subscriber_1.subscribe(con, L_RCBREF_STATUS) + + rcb_subscriber_2 = PyRCBSubscriber() + rcb_subscriber_2.subscribe(con, L_RCBREF_MEASUREMENTS) + + time.sleep(3) + + rcb_subscriber_1.destroy() + rcb_subscriber_2.destroy() + + except RuntimeError as caught_exception: + print(f"exception: {caught_exception}") + except AssertionError as caught_exception: + print(f"exception: {caught_exception}") + + close_connection(con) +else: + print("Connection error") + +destroy_connection(con) From 9963253bcc0e65206b580c94cb39c8e8674bcb5b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 20 Jan 2023 15:57:41 +0000 Subject: [PATCH 024/214] - fixed - IedConnection_setRCBValuesAsync crashes when RCB is already reserved by other client (LIB61850-382) --- src/iec61850/client/client_report_control.c | 8 +++++++- src/iec61850/client/ied_connection.c | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/iec61850/client/client_report_control.c b/src/iec61850/client/client_report_control.c index e126d570..6affd50e 100644 --- a/src/iec61850/client/client_report_control.c +++ b/src/iec61850/client/client_report_control.c @@ -808,8 +808,9 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD handler(param->originalInvokeId, call->callbackParameter, err); releaseWriteCall(self, call, param); - } + goto exit_function; + } param->currentItemId = LinkedList_getNext(param->currentItemId); @@ -829,6 +830,7 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD MmsConnection_writeVariableAsync(self->connection, &(call->invokeId), &writeError, param->domainId, itemId, value, writeVariableHandler, self); if (writeError != MMS_ERROR_NONE) { + handler(param->originalInvokeId, call->callbackParameter, iedConnection_mapMmsErrorToIedError(writeError)); releaseWriteCall(self, call, param); @@ -839,6 +841,10 @@ writeVariableHandler(uint32_t invokeId, void* parameter, MmsError mmsError, MmsD if (DEBUG_IED_CLIENT) printf("IED_CLIENT: internal error - no matching outstanding call!\n"); } + +exit_function: + + return; } uint32_t diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index 807037be..b49a1fe7 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -105,6 +105,9 @@ iedConnection_mapMmsErrorToIedError(MmsError mmsError) case MMS_ERROR_DEFINITION_OBJECT_UNDEFINED: return IED_ERROR_OBJECT_UNDEFINED; + case MMS_ERROR_ACCESS_TEMPORARILY_UNAVAILABLE: + return IED_ERROR_TEMPORARILY_UNAVAILABLE; + default: return IED_ERROR_UNKNOWN; } From 94e11ac2e3068da4457844b3ed35c87e43d60206 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 30 Jan 2023 18:33:01 +0000 Subject: [PATCH 025/214] - fixed - server send invalid response- when client uses wrong ctlModel (LIB61850-383) (#435) --- src/iec61850/server/mms_mapping/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index 15cb6a32..be24afee 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -1872,7 +1872,7 @@ Control_readAccessControlObject(MmsMapping* self, MmsDomain* domain, char* varia if (DEBUG_IED_SERVER) printf("IED_SERVER: select not applicable for control model %u\n", controlObject->ctlModel); - value = controlObject->sbo; + value = &emptyString; } } From f7ff7d0675e473cba03e6b7e767a69367cec0ea7 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 15 Feb 2023 14:14:16 +0100 Subject: [PATCH 026/214] - fixed - .NET: IedConenction.WriteDataSetValues throws a NullReferenceException (LIB61850-384) --- dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 1403cbec..4b45fe54 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -1993,7 +1993,6 @@ namespace IEC61850 if (accessResults != IntPtr.Zero) { - IntPtr element = LinkedList_getNext(accessResults); while (element != IntPtr.Zero) @@ -2004,6 +2003,9 @@ namespace IEC61850 MmsDataAccessError dataAccessError = accessResultValue.GetDataAccessError(); + if (accessResultList == null) + accessResultList = new List(); + accessResultList.Add(dataAccessError); element = LinkedList_getNext(element); From ee9c64adc84dad732971309ee5b61bb16c1afebf Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 22 Feb 2023 16:44:06 +0000 Subject: [PATCH 027/214] - fixed - MMS server: messages can be corrupted when TCP buffer is full (LIB61850-385) --- examples/mms_utility/mms_utility.c | 45 ++++++++++++-- src/mms/inc_private/cotp.h | 3 + src/mms/iso_cotp/cotp.c | 94 +++++++++++++++++++---------- src/mms/iso_server/iso_connection.c | 9 ++- src/mms/iso_server/iso_server.c | 1 - 5 files changed, 110 insertions(+), 42 deletions(-) diff --git a/examples/mms_utility/mms_utility.c b/examples/mms_utility/mms_utility.c index 8b43d9d7..24c909c9 100644 --- a/examples/mms_utility/mms_utility.c +++ b/examples/mms_utility/mms_utility.c @@ -101,6 +101,7 @@ printRawMmsMessage(void* parameter, uint8_t* message, int messageLength, bool re int main(int argc, char** argv) { + int returnCode = 0; char* hostname = StringUtils_copyString("localhost"); int tcpPort = 102; @@ -213,6 +214,10 @@ int main(int argc, char** argv) if (!MmsConnection_connect(con, &error, hostname, tcpPort)) { printf("MMS connect failed!\n"); + + if (error != MMS_ERROR_NONE) + returnCode = error; + goto exit; } else @@ -222,6 +227,9 @@ int main(int argc, char** argv) MmsServerIdentity* identity = MmsConnection_identify(con, &error); + if (error != MMS_ERROR_NONE) + returnCode = error; + if (identity != NULL) { printf("\nServer identity:\n----------------\n"); printf(" vendor:\t%s\n", identity->vendorName); @@ -235,14 +243,23 @@ int main(int argc, char** argv) if (readDeviceList) { printf("\nDomains present on server:\n--------------------------\n"); LinkedList nameList = MmsConnection_getDomainNames(con, &error); - LinkedList_printStringList(nameList); - LinkedList_destroy(nameList); + + if (error != MMS_ERROR_NONE) + returnCode = error; + + if (nameList) { + LinkedList_printStringList(nameList); + LinkedList_destroy(nameList); + } } if (getDeviceDirectory) { LinkedList variableList = MmsConnection_getDomainVariableNames(con, &error, domainName); + if (error != MMS_ERROR_NONE) + returnCode = error; + if (variableList) { LinkedList element = LinkedList_getNext(variableList); @@ -264,6 +281,9 @@ int main(int argc, char** argv) variableList = MmsConnection_getDomainJournals(con, &error, domainName); + if (error != MMS_ERROR_NONE) + returnCode = error; + if (variableList) { LinkedList element = variableList; @@ -309,6 +329,9 @@ int main(int argc, char** argv) LinkedList journalEntries = MmsConnection_readJournalTimeRange(con, &error, logDomain, logName, startTime, endTime, &moreFollows); + if (error != MMS_ERROR_NONE) + returnCode = error; + MmsValue_delete(startTime); MmsValue_delete(endTime); @@ -375,6 +398,8 @@ int main(int argc, char** argv) if (error != MMS_ERROR_NONE) { printf("Reading variable failed: (ERROR %i)\n", error); + + returnCode = error; } else { printf("Read SUCCESS\n"); @@ -403,6 +428,8 @@ int main(int argc, char** argv) if (error != MMS_ERROR_NONE) { printf("Reading variable failed: (ERROR %i)\n", error); + + returnCode = error; } else { printf("Read SUCCESS\n"); @@ -421,6 +448,8 @@ int main(int argc, char** argv) if (error != MMS_ERROR_NONE) { printf("Reading variable list directory failed: (ERROR %i)\n", error); + + returnCode = error; } else { LinkedList varListElem = LinkedList_getNext(varListDir); @@ -454,12 +483,19 @@ int main(int argc, char** argv) char* continueAfter = NULL; while (MmsConnection_getFileDirectory(con, &error, "", continueAfter, mmsFileDirectoryHandler, lastName)) { + + if (error != MMS_ERROR_NONE) + returnCode = error; + continueAfter = lastName; } } if (getFileAttributes) { MmsConnection_getFileDirectory(con, &error, filename, NULL, mmsGetFileAttributeHandler, NULL); + + if (error != MMS_ERROR_NONE) + returnCode = error; } if (deleteFile) { @@ -467,13 +503,14 @@ int main(int argc, char** argv) if (error != MMS_ERROR_NONE) { printf("Delete file failed: (ERROR %i)\n", error); + returnCode = error; } else { printf("File deleted\n"); } } - exit: +exit: free(hostname); free(domainName); free(variableName); @@ -482,6 +519,6 @@ int main(int argc, char** argv) MmsConnection_destroy(con); - return 0; + return returnCode; } diff --git a/src/mms/inc_private/cotp.h b/src/mms/inc_private/cotp.h index b7877cc8..3cbe2681 100644 --- a/src/mms/inc_private/cotp.h +++ b/src/mms/inc_private/cotp.h @@ -114,4 +114,7 @@ CotpConnection_getRemoteRef(CotpConnection* self); LIB61850_INTERNAL int CotpConnection_getLocalRef(CotpConnection* self); +LIB61850_INTERNAL void +CotpConnection_flushBuffer(CotpConnection* self); + #endif /* COTP_H_ */ diff --git a/src/mms/iso_cotp/cotp.c b/src/mms/iso_cotp/cotp.c index f57e05bc..2b4e43e6 100644 --- a/src/mms/iso_cotp/cotp.c +++ b/src/mms/iso_cotp/cotp.c @@ -5,7 +5,7 @@ * * Partial implementation of the ISO 8073 COTP (ISO TP0) protocol for MMS. * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -174,6 +174,38 @@ writeToSocket(CotpConnection* self, uint8_t* buf, int size) #endif } +static bool +flushBuffer(CotpConnection* self) +{ + if (self->socketExtensionBufferFill > 0) { + + int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill); + + if (sentBytes > 0) { + + if (sentBytes != self->socketExtensionBufferFill) { + int target = 0; + int i; + uint8_t* buf = self->socketExtensionBuffer; + + for (i = sentBytes; i < self->socketExtensionBufferFill; i++) { + buf[target++] = buf[i]; + } + + self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes; + } + else { + self->socketExtensionBufferFill = 0; + } + } + else if (sentBytes == -1) { + return false; + } + } + + return true; +} + static bool sendBuffer(CotpConnection* self) { @@ -182,7 +214,15 @@ sendBuffer(CotpConnection* self) bool retVal = false; - int sentBytes = writeToSocket(self, buffer, remainingSize); + if (flushBuffer(self) == false) { + goto exit_function; + } + + int sentBytes = 0; + + if (self->socketExtensionBufferFill == 0) { + sentBytes = writeToSocket(self, buffer, remainingSize); + } if (sentBytes == -1) goto exit_function; @@ -215,33 +255,6 @@ exit_function: return retVal; } -static void -flushBuffer(CotpConnection* self) -{ - if (self->socketExtensionBufferFill > 0) { - - int sentBytes = writeToSocket(self, self->socketExtensionBuffer, self->socketExtensionBufferFill); - - if (sentBytes > 0) { - - if (sentBytes != self->socketExtensionBufferFill) { - int target = 0; - int i; - uint8_t* buf = self->socketExtensionBuffer; - - for (i = sentBytes; i < self->socketExtensionBufferFill; i++) { - buf[target++] = buf[i]; - } - - self->socketExtensionBufferFill = self->socketExtensionBufferFill - sentBytes; - } - else { - self->socketExtensionBufferFill = 0; - } - } - } -} - CotpIndication CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) { @@ -262,7 +275,9 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) int totalSize = (fragments * (COTP_DATA_HEADER_SIZE + 4)) + payload->length; /* try to flush extension buffer */ - flushBuffer(self); + if (flushBuffer(self) == false) { + return COTP_ERROR; + } /* check if totalSize will fit in extension buffer */ if (self->socketExtensionBuffer) { @@ -281,7 +296,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) int currentChainIndex = 0; if (DEBUG_COTP) - printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); + printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); uint8_t* buffer = self->writeBuffer->buffer; @@ -307,7 +322,7 @@ CotpConnection_sendDataMessage(CotpConnection* self, BufferChain payload) if (currentChainIndex >= currentChain->partLength) { currentChain = currentChain->nextPart; if (DEBUG_COTP) - printf("\nCOTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); + printf("COTP: nextBufferPart: len:%i partLen:%i\n", currentChain->length, currentChain->partLength); currentChainIndex = 0; } @@ -756,6 +771,13 @@ readFromSocket(CotpConnection* self, uint8_t* buf, int size) #endif } +void +CotpConnection_flushBuffer(CotpConnection* self) +{ + if (self->socketExtensionBufferFill > 0) + flushBuffer(self); +} + TpktState CotpConnection_readToTpktBuffer(CotpConnection* self) { @@ -765,6 +787,14 @@ CotpConnection_readToTpktBuffer(CotpConnection* self) assert (bufferSize > 4); + if (self->socketExtensionBufferFill > 0) { + if (flushBuffer(self) == false) + goto exit_error; + + if (self->socketExtensionBufferFill > 0) + goto exit_waiting; + } + int readBytes; if (bufPos < 4) { diff --git a/src/mms/iso_server/iso_connection.c b/src/mms/iso_server/iso_connection.c index 7f997a9b..795b0636 100644 --- a/src/mms/iso_server/iso_connection.c +++ b/src/mms/iso_server/iso_connection.c @@ -1,7 +1,7 @@ /* * iso_connection.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -160,6 +160,8 @@ IsoConnection_removeFromHandleSet(const IsoConnection self, HandleSet handles) void IsoConnection_callTickHandler(IsoConnection self) { + CotpConnection_flushBuffer(self->cotpConnection); + if (self->tickHandler) { self->tickHandler(self->handlerParameter); } @@ -171,10 +173,7 @@ IsoConnection_handleTcpConnection(IsoConnection self, bool isSingleThread) #if (CONFIG_MMS_SINGLE_THREADED != 1) if (isSingleThread == false) { - /* call tick handler */ - if (self->tickHandler) { - self->tickHandler(self->handlerParameter); - } + IsoConnection_callTickHandler(self); if (Handleset_waitReady(self->handleSet, 10) < 1) goto exit_function; diff --git a/src/mms/iso_server/iso_server.c b/src/mms/iso_server/iso_server.c index 62c43ebb..a2daa6ad 100644 --- a/src/mms/iso_server/iso_server.c +++ b/src/mms/iso_server/iso_server.c @@ -455,7 +455,6 @@ exit_function: return success; } - /** used by single and multi-threaded versions * * \param isSingleThread when true server is running in single thread or non-thread mode From e77bd15134ad8f3cfb1d5ee2c2b51789c9bbe4e8 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 24 Feb 2023 07:31:48 +0000 Subject: [PATCH 028/214] - added new callback function headers for dataset access (LIB61850-387) --- src/iec61850/inc/iec61850_server.h | 15 +++++++++++ src/mms/inc/mms_server.h | 23 ++++++++++++++++ src/mms/inc_private/mms_server_internal.h | 2 +- .../iso_mms/server/mms_named_variable_list.c | 27 +++++++++---------- 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index e3790011..9d34084c 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1841,6 +1841,21 @@ typedef MmsDataAccessError LIB61850_API void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); + +typedef enum { + DATASET_CREATE, + DATASET_DELETE, + DATASET_READ, + DATASET_WRITE, + DATASET_GET_DIRECTORY +} IedServer_DataSetOperation; + +typedef bool +(*IedServer_DataSetAccessHandler) (void* parameter, ClientConnection connection, IedServer_DataSetOperation operation, const char* datasetRef); + +LIB61850_API void +IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 98e4c796..2986d5b3 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -57,6 +57,29 @@ MmsServer_setLocalIpAddress(MmsServer self, const char* localIpAddress); LIB61850_INTERNAL bool MmsServer_isRunning(MmsServer self); +typedef enum { + MMS_VARLIST_CREATE, + MMS_VARLIST_DELETE, + MMS_VARLIST_READ, + MMS_VARLIST_WRITE, + MMS_VARLIST_GET_DIRECTORY +} MmsVariableListAccessType; + +/** + * \brief callback handler that is called for each named variable list access + * + * \param parameter a user provided parameter + * \param accessType the kind of access (create, delete, read, write, get directory) + * \param listType the type (scope) of the named variable list (either domain, association or VMD specific) + * \param domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!) + * \param listName the name + * \param connection client connection that is accessing the named variable list + * + * \return MMS_ERROR_NONE if the request is accepted, otherwise the MmsError value that has to be sent back to the client + */ +typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, + char* listName, MmsServerConnection connection); + /** * \brief callback handler that is called whenever a named variable list changes * diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 727e97a3..bbf5195f 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -123,7 +123,7 @@ struct sMmsServer { MmsConnectionHandler connectionHandler; void* connectionHandlerParameter; - MmsNamedVariableListChangedHandler variableListChangedHandler; /* TODO this is only required if dynamic data sets are supported! */ + MmsNamedVariableListChangedHandler variableListChangedHandler; void* variableListChangedHandlerParameter; AcseAuthenticator authenticator; diff --git a/src/mms/iso_mms/server/mms_named_variable_list.c b/src/mms/iso_mms/server/mms_named_variable_list.c index 31694780..3c5ea5c3 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list.c +++ b/src/mms/iso_mms/server/mms_named_variable_list.c @@ -3,22 +3,22 @@ * * Copyright 2013 Michael Zillgith * - * This file is part of libIEC61850. + * This file is part of libIEC61850. * - * libIEC61850 is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. * - * libIEC61850 is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with libIEC61850. If not, see . + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . * - * See COPYING file for the complete license text. + * See COPYING file for the complete license text. */ #include "libiec61850_platform_includes.h" @@ -51,7 +51,6 @@ MmsNamedVariableListEntry_destroy(MmsNamedVariableListEntry self) GLOBAL_FREEMEM(self); } - MmsDomain* MmsNamedVariableListEntry_getDomain(MmsNamedVariableListEntry self) { @@ -120,5 +119,3 @@ MmsNamedVariableList_destroy(MmsNamedVariableList self) GLOBAL_FREEMEM(self->name); GLOBAL_FREEMEM(self); } - - From cb87b8d9ff88e52343b34e93e9502908c2c00804 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 24 Feb 2023 11:07:09 +0000 Subject: [PATCH 029/214] - added missing checks in iec61850_client_example_control (#442) --- .../client_example_control.c | 183 ++++++++++-------- 1 file changed, 106 insertions(+), 77 deletions(-) diff --git a/examples/iec61850_client_example_control/client_example_control.c b/examples/iec61850_client_example_control/client_example_control.c index 33da635a..37d6b6bc 100644 --- a/examples/iec61850_client_example_control/client_example_control.c +++ b/examples/iec61850_client_example_control/client_example_control.c @@ -45,8 +45,10 @@ int main(int argc, char** argv) { IedConnection_connect(con, &error, hostname, tcpPort); - if (error == IED_ERROR_OK) { - + if (error == IED_ERROR_OK) + { + MmsValue* ctlVal = NULL; + MmsValue* stVal = NULL; /************************ * Direct control @@ -55,99 +57,116 @@ int main(int argc, char** argv) { ControlObjectClient control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO1", con); - MmsValue* ctlVal = MmsValue_newBoolean(true); + if (control) + { + ctlVal = MmsValue_newBoolean(true); - ControlObjectClient_setOrigin(control, NULL, 3); + ControlObjectClient_setOrigin(control, NULL, 3); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n"); - } - else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n"); - } + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO1 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO1\n"); + } + + MmsValue_delete(ctlVal); - MmsValue_delete(ctlVal); + ControlObjectClient_destroy(control); - ControlObjectClient_destroy(control); + /* Check if status value has changed */ - /* Check if status value has changed */ + stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST); - MmsValue* stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO1.stVal", IEC61850_FC_ST); + if (error == IED_ERROR_OK) { + bool state = MmsValue_getBoolean(stVal); + MmsValue_delete(stVal); - if (error == IED_ERROR_OK) { - bool state = MmsValue_getBoolean(stVal); - MmsValue_delete(stVal); + printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state); + } + else { + printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n"); + } - printf("New status of simpleIOGenericIO/GGIO1.SPCSO1.stVal: %i\n", state); } else { - printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO1 failed!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO1 not found in server\n"); } - /************************ * Select before operate ***********************/ control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO2", con); - if (ControlObjectClient_select(control)) { + if (control) + { + if (ControlObjectClient_select(control)) { - ctlVal = MmsValue_newBoolean(true); + ctlVal = MmsValue_newBoolean(true); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n"); + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO2 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n"); + } + + MmsValue_delete(ctlVal); } else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO2!\n"); + printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n"); } - MmsValue_delete(ctlVal); + ControlObjectClient_destroy(control); } else { - printf("failed to select simpleIOGenericIO/GGIO1.SPCSO2!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO2 not found in server\n"); } - ControlObjectClient_destroy(control); - - /**************************************** * Direct control with enhanced security ****************************************/ control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO3", con); - ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); + if (control) + { + ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - ctlVal = MmsValue_newBoolean(true); + ctlVal = MmsValue_newBoolean(true); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n"); - } - else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n"); - } + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO3 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO3\n"); + } - MmsValue_delete(ctlVal); + MmsValue_delete(ctlVal); - /* Wait for command termination message */ - Thread_sleep(1000); + /* Wait for command termination message */ + Thread_sleep(1000); - ControlObjectClient_destroy(control); + ControlObjectClient_destroy(control); - /* Check if status value has changed */ + /* Check if status value has changed */ - stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST); + stVal = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.SPCSO3.stVal", IEC61850_FC_ST); - if (error == IED_ERROR_OK) { - bool state = MmsValue_getBoolean(stVal); + if (error == IED_ERROR_OK) { + bool state = MmsValue_getBoolean(stVal); - printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state); + printf("New status of simpleIOGenericIO/GGIO1.SPCSO3.stVal: %i\n", state); - MmsValue_delete(stVal); + MmsValue_delete(stVal); + } + else { + printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n"); + } } else { - printf("Reading status for simpleIOGenericIO/GGIO1.SPCSO3 failed!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO3 not found in server\n"); } /*********************************************** @@ -156,56 +175,66 @@ int main(int argc, char** argv) { control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO4", con); - ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); + if (control) + { + ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - ctlVal = MmsValue_newBoolean(true); + ctlVal = MmsValue_newBoolean(true); - if (ControlObjectClient_selectWithValue(control, ctlVal)) { + if (ControlObjectClient_selectWithValue(control, ctlVal)) { + + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n"); + } - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO4 operated successfully\n"); } else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO4!\n"); + printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n"); } + MmsValue_delete(ctlVal); + + /* Wait for command termination message */ + Thread_sleep(1000); + + ControlObjectClient_destroy(control); } else { - printf("failed to select simpleIOGenericIO/GGIO1.SPCSO4!\n"); + printf("Control object simpleIOGenericIO/GGIO1.SPCSO4 not found in server\n"); } - MmsValue_delete(ctlVal); - - /* Wait for command termination message */ - Thread_sleep(1000); - - ControlObjectClient_destroy(control); - - /********************************************************************* * Direct control with enhanced security (expect CommandTermination-) *********************************************************************/ control = ControlObjectClient_create("simpleIOGenericIO/GGIO1.SPCSO9", con); - ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - - ctlVal = MmsValue_newBoolean(true); + if (control) + { + ControlObjectClient_setCommandTerminationHandler(control, commandTerminationHandler, NULL); - if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { - printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n"); - } - else { - printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n"); - } + ctlVal = MmsValue_newBoolean(true); - MmsValue_delete(ctlVal); + if (ControlObjectClient_operate(control, ctlVal, 0 /* operate now */)) { + printf("simpleIOGenericIO/GGIO1.SPCSO9 operated successfully\n"); + } + else { + printf("failed to operate simpleIOGenericIO/GGIO1.SPCSO9\n"); + } - /* Wait for command termination message */ - Thread_sleep(1000); + MmsValue_delete(ctlVal); - ControlObjectClient_destroy(control); + /* Wait for command termination message */ + Thread_sleep(1000); + ControlObjectClient_destroy(control); + } + else { + printf("Control object simpleIOGenericIO/GGIO1.SPCSO9 not found in server\n"); + } IedConnection_close(con); } From 9fa68a9d9395fabeb4716f870a1c1c6f1c5869fb Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 24 Feb 2023 17:09:03 +0000 Subject: [PATCH 030/214] - IED server: added ControlAction_getSynchroCheck and ControlAction_getInterlockCheck functions --- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 18 +++++++++++++++++ src/iec61850/inc/iec61850_server.h | 20 +++++++++++++++++++ src/iec61850/server/mms_mapping/control.c | 16 +++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index aefd1c7b..f9588668 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -1866,6 +1866,14 @@ namespace IEC61850 [return: MarshalAs(UnmanagedType.I1)] static extern bool ControlAction_isSelect(IntPtr self); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ControlAction_getSynchroCheck(IntPtr self); + + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.I1)] + static extern bool ControlAction_getInterlockCheck(IntPtr self); + private IntPtr self; private IedServer.ControlHandlerInfo info; private IedServer iedServer; @@ -1985,6 +1993,16 @@ namespace IEC61850 { return ControlAction_isSelect(self); } + + public bool GetSynchroCheck() + { + return ControlAction_getSynchroCheck(self); + } + + public bool GetInterlockCheck() + { + return ControlAction_getInterlockCheck(self); + } } public delegate void GoCBEventHandler(MmsGooseControlBlock goCB, int cbEvent, object parameter); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index e3790011..a0a5b56f 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1365,6 +1365,26 @@ ControlAction_getOrIdent(ControlAction self, int* orIdentSize); LIB61850_API int ControlAction_getCtlNum(ControlAction self); +/** + * \brief Gets the synchroCheck bit provided by the client + * + * \param self the control action instance + * + * \return the synchroCheck bit + */ +LIB61850_API bool +ControlAction_getSynchroCheck(ControlAction self); + +/** + * \brief Gets the interlockCheck bit provided by the client + * + * \param self the control action instance + * + * \return the interlockCheck bit + */ +LIB61850_API bool +ControlAction_getInterlockCheck(ControlAction self); + /** * \brief Check if the control callback is called by a select or operate command * diff --git a/src/iec61850/server/mms_mapping/control.c b/src/iec61850/server/mms_mapping/control.c index be24afee..de2b6f89 100644 --- a/src/iec61850/server/mms_mapping/control.c +++ b/src/iec61850/server/mms_mapping/control.c @@ -2498,6 +2498,22 @@ ControlAction_getCtlNum(ControlAction self) return -1; } +bool +ControlAction_getSynchroCheck(ControlAction self) +{ + ControlObject* controlObject = (ControlObject*) self; + + return (bool)(controlObject->synchroCheck); +} + +bool +ControlAction_getInterlockCheck(ControlAction self) +{ + ControlObject* controlObject = (ControlObject*) self; + + return (bool)(controlObject->interlockCheck); +} + bool ControlAction_isSelect(ControlAction self) { From 39a55f672cb056167a4ffd9a30e13e11283ca2e0 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 24 Feb 2023 19:15:48 +0000 Subject: [PATCH 031/214] - MMS server: replaced MmsNamedVariableListChangedHandler by more generic MmsNamedVariableListAccessHandler (LIB61850-387) - MMS server: implemented dataset access handler callback in dataset read access (LIB61850-387) --- src/iec61850/server/mms_mapping/mms_mapping.c | 35 ++++++++--- src/mms/inc/mms_server.h | 19 +----- src/mms/inc_private/mms_server_internal.h | 6 +- .../server/mms_named_variable_list_service.c | 24 +++---- src/mms/iso_mms/server/mms_read_service.c | 63 ++++++++++++++----- src/mms/iso_mms/server/mms_server.c | 6 +- 6 files changed, 96 insertions(+), 57 deletions(-) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index fc080ea2..4d6d8b31 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -1,7 +1,7 @@ /* * mms_mapping.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -3379,7 +3379,7 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } static MmsError -variableListChangedHandler (void* parameter, bool create, MmsVariableListType listType, MmsDomain* domain, +variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection) { MmsError allow = MMS_ERROR_NONE; @@ -3389,10 +3389,16 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li /* TODO add log message */ #if (DEBUG_IED_SERVER == 1) - if (create) + if (accessType == MMS_VARLIST_CREATE) printf("IED_SERVER: create data set "); - else + else if (accessType == MMS_VARLIST_DELETE) printf("IED_SERVER: delete data set "); + else if (accessType == MMS_VARLIST_READ) + printf("IED_SERVER: read data set "); + else if (accessType == MMS_VARLIST_WRITE) + printf("IED_SERVER: write data set "); + else if (accessType == MMS_VARLIST_READ) + printf("IED_SERVER: get directory of data set "); switch (listType) { case MMS_VMD_SPECIFIC: @@ -3411,7 +3417,10 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li MmsMapping* self = (MmsMapping*) parameter; - if (create) { + if (accessType == MMS_VARLIST_CREATE) { + + //TODO call user callback hander (IedServer_DataSetAccessHandler) + if (listType == MMS_DOMAIN_SPECIFIC) { /* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */ @@ -3441,7 +3450,10 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li } } - else { + else if (accessType == MMS_VARLIST_DELETE) { + + //TODO call user callback hander (IedServer_DataSetAccessHandler) + /* Check if data set is referenced in a report */ LinkedList rcElement = self->reportControls; @@ -3519,6 +3531,15 @@ variableListChangedHandler (void* parameter, bool create, MmsVariableListType li #endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ } + else if (accessType == MMS_VARLIST_READ) { + //TODO call user callback hander (IedServer_DataSetAccessHandler) + } + else if (accessType == MMS_VARLIST_WRITE) { + //TODO call user callback hander (IedServer_DataSetAccessHandler) + } + else if (accessType == MMS_VARLIST_GET_DIRECTORY) { + //TODO call user callback hander (IedServer_DataSetAccessHandler) + } return allow; } @@ -3530,7 +3551,7 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); - MmsServer_installVariableListChangedHandler(self->mmsServer, variableListChangedHandler, (void*) self); + MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); } void diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index 2986d5b3..b9c22417 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -81,29 +81,14 @@ typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariab char* listName, MmsServerConnection connection); /** - * \brief callback handler that is called whenever a named variable list changes - * - * \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 listType the type (scope) of the named variable list (either domain, association or VMD specific) - * \param domain the MMS domain the list is belonging to (is NULL for association or VMD specific lists!) - * \param listName the name - * \param connection client connection that requests the creation of deletion of the variable list - * - * \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, - char* listName, MmsServerConnection connection); - -/** - * \brief Install callback handler that is called when a named variable list changes (is created or deleted) + * \brief Install callback handler that is called when a named variable list is accessed by a client * * \param self the MmsServer instance to operate on * \param handler the callback handler function * \param parameter user provided parameter that is passed to the callback handler */ LIB61850_INTERNAL void -MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter); +MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter); /** * \brief ObtainFile service callback handler diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index bbf5195f..fc875597 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -123,8 +123,8 @@ struct sMmsServer { MmsConnectionHandler connectionHandler; void* connectionHandlerParameter; - MmsNamedVariableListChangedHandler variableListChangedHandler; - void* variableListChangedHandlerParameter; + MmsNamedVariableListAccessHandler variableListAccessHandler; + void* variableListAccessHandlerParameter; AcseAuthenticator authenticator; void* authenticatorParameter; @@ -421,7 +421,7 @@ mmsServer_createMmsWriteResponse(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); LIB61850_INTERNAL MmsError -mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain, +mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection); #endif /* MMS_SERVER_INTERNAL_H_ */ diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c index 3a27061c..4baf4ab3 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list_service.c +++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c @@ -1,7 +1,7 @@ /* * mms_named_variable_list_service.c * - * Copyright 2013-2020 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -49,17 +49,17 @@ #endif MmsError -mmsServer_callVariableListChangedHandler(bool create, MmsVariableListType listType, MmsDomain* domain, +mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection) { MmsServer self = connection->server; - if (self->variableListChangedHandler != NULL) { + if (self->variableListAccessHandler != NULL) { if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: call MmsNamedVariableListChangedHandler for new list %s\n", listName); + printf("MMS_SERVER: call MmsNamedVariableListAccessHandler for new list %s\n", listName); - return self->variableListChangedHandler(self->variableListChangedHandlerParameter, - create, listType, domain, listName, connection); + return self->variableListAccessHandler(self->variableListAccessHandlerParameter, + accessType, listType, domain, listName, connection); } else return MMS_ERROR_NONE; @@ -180,7 +180,7 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, if (MmsNamedVariableList_isDeletable(variableList)) { - MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_DOMAIN_SPECIFIC, domain, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_DOMAIN_SPECIFIC, domain, listName, connection); if (deleteError == MMS_ERROR_NONE) { MmsDomain_deleteNamedVariableList(domain, listName); @@ -203,7 +203,7 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, if (variableList != NULL) { numberMatched++; - MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection); if (deleteError == MMS_ERROR_NONE) { numberDeleted++; @@ -224,7 +224,7 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, if (variableList != NULL) { numberMatched++; - MmsError deleteError = mmsServer_callVariableListChangedHandler(false, MMS_VMD_SPECIFIC, NULL, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_VMD_SPECIFIC, NULL, listName, connection); if (deleteError == MMS_ERROR_NONE) { numberDeleted++; @@ -526,7 +526,7 @@ mmsServer_handleDefineNamedVariableListRequest( if (namedVariableList != NULL) { - mmsError = mmsServer_callVariableListChangedHandler(true, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection); + mmsError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection); if (mmsError == MMS_ERROR_NONE) { MmsDomain_addNamedVariableList(domain, namedVariableList); @@ -576,7 +576,7 @@ mmsServer_handleDefineNamedVariableListRequest( if (namedVariableList != NULL) { - if (mmsServer_callVariableListChangedHandler(true, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) { + if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) { MmsServerConnection_addNamedVariableList(connection, namedVariableList); createDefineNamedVariableListResponse(invokeId, response); } @@ -619,7 +619,7 @@ mmsServer_handleDefineNamedVariableListRequest( request, variableListName, &mmsError); if (namedVariableList != NULL) { - if (mmsServer_callVariableListChangedHandler(true, MMS_VMD_SPECIFIC, NULL, variableListName, connection) + if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_VMD_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) { LinkedList_add(vmdScopeNVLs, (void*) namedVariableList); diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 2638bc0b..3193c699 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -768,9 +768,21 @@ handleReadNamedVariableListRequest( else { MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); - if (namedList != NULL) { - createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), - &accessSpec); + if (namedList) + { + + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_DOMAIN_SPECIFIC, domain, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) { + createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), + &accessSpec); + } + else { + if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s access error: %i\n", nameIdStr, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } else { if (DEBUG_MMS_SERVER) printf("MMS read: named variable list %s not found!\n", nameIdStr); @@ -791,14 +803,24 @@ handleReadNamedVariableListRequest( mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); else { - VarAccessSpec accessSpec; + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_VMD_SPECIFIC, NULL, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) { + VarAccessSpec accessSpec; - accessSpec.isNamedVariableList = true; - accessSpec.specific = 0; - accessSpec.domainId = NULL; - accessSpec.itemId = listName; + accessSpec.isNamedVariableList = true; + accessSpec.specific = 0; + accessSpec.domainId = NULL; + accessSpec.itemId = listName; + + createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + } + else { + if (DEBUG_MMS_SERVER) printf("MMS read: VMD specific named variable list %s access error: %i\n", listName, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } - createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); } } #if (MMS_DYNAMIC_DATA_SETS == 1) @@ -815,14 +837,25 @@ handleReadNamedVariableListRequest( if (namedList == NULL) mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); else { - VarAccessSpec accessSpec; - accessSpec.isNamedVariableList = true; - accessSpec.specific = 2; - accessSpec.domainId = NULL; - accessSpec.itemId = listName; + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_READ, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) { + + VarAccessSpec accessSpec; - createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + accessSpec.isNamedVariableList = true; + accessSpec.specific = 2; + accessSpec.domainId = NULL; + accessSpec.itemId = listName; + + createNamedVariableListResponse(connection, namedList, invokeId, response, isSpecWithResult(read), &accessSpec); + } + else { + if (DEBUG_MMS_SERVER) printf("MMS read: association specific named variable list %s access error: %i\n", listName, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } } } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index ed2cf725..e6cccfa5 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -359,10 +359,10 @@ MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connecti } void -MmsServer_installVariableListChangedHandler(MmsServer self, MmsNamedVariableListChangedHandler handler, void* parameter) +MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListAccessHandler handler, void* parameter) { - self->variableListChangedHandler = handler; - self->variableListChangedHandlerParameter = parameter; + self->variableListAccessHandler = handler; + self->variableListAccessHandlerParameter = parameter; } void From acfbe16452256d04472c4429ef28f456d094cb2f Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 27 Feb 2023 16:55:03 +0000 Subject: [PATCH 032/214] - IED server/MMS server: added data set access callbacks and example (LIB61850-387) --- examples/CMakeLists.txt | 1 + .../CMakeLists.txt | 21 + .../server_example_access_control/Makefile | 32 + .../server_example_access_control.c | 243 ++ .../static_model.c | 2223 +++++++++++++++++ .../static_model.h | 311 +++ .../inc_private/mms_mapping_internal.h | 3 + src/iec61850/server/impl/ied_server.c | 7 + src/iec61850/server/mms_mapping/mms_mapping.c | 193 +- src/mms/inc/iso_connection_parameters.h | 6 +- .../server/mms_named_variable_list_service.c | 767 +++--- src/mms/iso_mms/server/mms_write_service.c | 47 +- 12 files changed, 3410 insertions(+), 444 deletions(-) create mode 100644 examples/server_example_access_control/CMakeLists.txt create mode 100644 examples/server_example_access_control/Makefile create mode 100644 examples/server_example_access_control/server_example_access_control.c create mode 100644 examples/server_example_access_control/static_model.c create mode 100644 examples/server_example_access_control/static_model.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d787fb9b..899cd7b7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(server_example_files) add_subdirectory(server_example_substitution) add_subdirectory(server_example_service_tracking) add_subdirectory(server_example_deadband) +add_subdirectory(server_example_access_control) add_subdirectory(iec61850_client_example1) add_subdirectory(iec61850_client_example2) diff --git a/examples/server_example_access_control/CMakeLists.txt b/examples/server_example_access_control/CMakeLists.txt new file mode 100644 index 00000000..a71a7383 --- /dev/null +++ b/examples/server_example_access_control/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories( + . +) + +set(server_example_SRCS + server_example_access_control.c + static_model.c +) + +IF(MSVC) +set_source_files_properties(${server_example_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(MSVC) + +add_executable(server_example_access_control + ${server_example_SRCS} +) + +target_link_libraries(server_example_access_control + iec61850 +) diff --git a/examples/server_example_access_control/Makefile b/examples/server_example_access_control/Makefile new file mode 100644 index 00000000..4b288a51 --- /dev/null +++ b/examples/server_example_access_control/Makefile @@ -0,0 +1,32 @@ +LIBIEC_HOME=../.. + +PROJECT_BINARY_NAME = server_example_access_control +PROJECT_SOURCES = server_example_access_control.c +PROJECT_SOURCES += static_model.c + +PROJECT_ICD_FILE = simpleIO_direct_control.cid + +include $(LIBIEC_HOME)/make/target_system.mk +include $(LIBIEC_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIBIEC_HOME)/make/common_targets.mk + +LDLIBS += -lm + +CP = cp + +model: $(PROJECT_ICD_FILE) + java -jar $(LIBIEC_HOME)/tools/model_generator/genmodel.jar $(PROJECT_ICD_FILE) + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + mkdir -p vmd-filestore + $(CP) $(PROJECT_BINARY_NAME) vmd-filestore/IEDSERVER.BIN + +clean: + rm -f $(PROJECT_BINARY_NAME) + rm -f vmd-filestore/IEDSERVER.BIN + + diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c new file mode 100644 index 00000000..024bf77a --- /dev/null +++ b/examples/server_example_access_control/server_example_access_control.c @@ -0,0 +1,243 @@ +/* + * server_example_access_control.c + * + * - How to use access control mechanisms + * - How to implement RBAC features based on access control mechanisms + */ + +#include "iec61850_server.h" +#include "hal_thread.h" +#include +#include +#include +#include + +#include "static_model.h" + +static int running = 0; +static IedServer iedServer = NULL; + +void +sigint_handler(int signalId) +{ + running = 0; +} + +static ControlHandlerResult +controlHandlerForBinaryOutput(ControlAction action, void* parameter, MmsValue* value, bool test) +{ + if (test) + return CONTROL_RESULT_FAILED; + + if (MmsValue_getType(value) == MMS_BOOLEAN) { + printf("received binary control command: "); + + if (MmsValue_getBoolean(value)) + printf("on\n"); + else + printf("off\n"); + } + else + return CONTROL_RESULT_FAILED; + + uint64_t timeStamp = Hal_getTimeInMs(); + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO1) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO2) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO3) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal, value); + } + + if (parameter == IEDMODEL_GenericIO_GGIO1_SPCSO4) { + IedServer_updateUTCTimeAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_t, timeStamp); + IedServer_updateAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal, value); + } + + return CONTROL_RESULT_OK; +} + + +static void +connectionHandler (IedServer self, ClientConnection connection, bool connected, void* parameter) +{ + if (connected) + printf("Connection opened\n"); + else + printf("Connection closed\n"); +} + +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; +} + +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); + + 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); + + IedServer_setDataSetAccessHandler(iedServer, dataSetAccessHandler, NULL); + + /* MMS server will be instructed to start listening for client connections. */ + IedServer_start(iedServer, tcpPort); + + if (!IedServer_isRunning(iedServer)) { + printf("Starting server failed (maybe need root permissions or another server is already using the port)! Exit.\n"); + IedServer_destroy(iedServer); + exit(-1); + } + + running = 1; + + signal(SIGINT, sigint_handler); + + float t = 0.f; + + while (running) { + uint64_t timestamp = Hal_getTimeInMs(); + + t += 0.1f; + + float an1 = sinf(t); + float an2 = sinf(t + 1.f); + float an3 = sinf(t + 2.f); + float an4 = sinf(t + 3.f); + + Timestamp iecTimestamp; + + Timestamp_clearFlags(&iecTimestamp); + Timestamp_setTimeInMilliseconds(&iecTimestamp, timestamp); + Timestamp_setLeapSecondKnown(&iecTimestamp, true); + + /* toggle clock-not-synchronized flag in timestamp */ + if (((int) t % 2) == 0) + Timestamp_setClockNotSynchronized(&iecTimestamp, true); + +#if 1 + IedServer_lockDataModel(iedServer); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f, an1); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f, an2); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f, an3); + + IedServer_updateTimestampAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_t, &iecTimestamp); + IedServer_updateFloatAttributeValue(iedServer, IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f, an4); + + IedServer_unlockDataModel(iedServer); +#endif + + Thread_sleep(100); + } + + /* stop MMS server - close TCP server socket and all client sockets */ + IedServer_stop(iedServer); + + /* Cleanup - free all resources */ + IedServer_destroy(iedServer); + + return 0; +} /* main() */ diff --git a/examples/server_example_access_control/static_model.c b/examples/server_example_access_control/static_model.c new file mode 100644 index 00000000..26f5f4fc --- /dev/null +++ b/examples/server_example_access_control/static_model.c @@ -0,0 +1,2223 @@ +/* + * static_model.c + * + * automatically generated from simpleIO_direct_control.cid + */ +#include "static_model.h" + +static void initializeValues(); + +extern DataSet iedModelds_GenericIO_LLN0_Events; +extern DataSet iedModelds_GenericIO_LLN0_Events2; +extern DataSet iedModelds_GenericIO_LLN0_Measurements; + + +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3$stVal", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4$stVal", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Events = { + "GenericIO", + "LLN0$Events", + 4, + &iedModelds_GenericIO_LLN0_Events_fcda0, + &iedModelds_GenericIO_LLN0_Events2 +}; + +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda0 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO1", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda1 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO2", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda2 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO3", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Events2_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Events2_fcda3 = { + "GenericIO", + false, + "GGIO1$ST$SPCSO4", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Events2 = { + "GenericIO", + "LLN0$Events2", + 4, + &iedModelds_GenericIO_LLN0_Events2_fcda0, + &iedModelds_GenericIO_LLN0_Measurements +}; + +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6; +extern DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda0 = { + "GenericIO", + false, + "GGIO1$MX$AnIn1$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda1 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda1 = { + "GenericIO", + false, + "GGIO1$MX$AnIn1$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda2 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda2 = { + "GenericIO", + false, + "GGIO1$MX$AnIn2$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda3 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda3 = { + "GenericIO", + false, + "GGIO1$MX$AnIn2$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda4 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda4 = { + "GenericIO", + false, + "GGIO1$MX$AnIn3$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda5 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda5 = { + "GenericIO", + false, + "GGIO1$MX$AnIn3$q", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda6 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda6 = { + "GenericIO", + false, + "GGIO1$MX$AnIn4$mag$f", + -1, + NULL, + NULL, + &iedModelds_GenericIO_LLN0_Measurements_fcda7 +}; + +DataSetEntry iedModelds_GenericIO_LLN0_Measurements_fcda7 = { + "GenericIO", + false, + "GGIO1$MX$AnIn4$q", + -1, + NULL, + NULL, + NULL +}; + +DataSet iedModelds_GenericIO_LLN0_Measurements = { + "GenericIO", + "LLN0$Measurements", + 8, + &iedModelds_GenericIO_LLN0_Measurements_fcda0, + NULL +}; + +LogicalDevice iedModel_GenericIO = { + LogicalDeviceModelType, + "GenericIO", + (ModelNode*) &iedModel, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL +}; + +LogicalNode iedModel_GenericIO_LLN0 = { + LogicalNodeModelType, + "LLN0", + (ModelNode*) &iedModel_GenericIO, + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, +}; + +DataObject iedModel_GenericIO_LLN0_Mod = { + DataObjectModelType, + "Mod", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + (ModelNode*) &iedModel_GenericIO_LLN0_Mod_ctlModel, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_LLN0_Mod, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + (ModelNode*) &iedModel_GenericIO_LLN0_Beh_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Beh, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_GenericIO_LLN0, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_Health_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Health_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + (ModelNode*) &iedModel_GenericIO_LLN0_Health_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_Health_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LLN0_Health, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LLN0_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_GenericIO_LLN0, + NULL, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_vendor, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_swRev, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_d, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_configRev, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev = { + DataAttributeModelType, + "configRev", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt_ldNs, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs = { + DataAttributeModelType, + "ldNs", + (ModelNode*) &iedModel_GenericIO_LLN0_NamPlt, + NULL, + NULL, + 0, + -1, + IEC61850_FC_EX, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +LogicalNode iedModel_GenericIO_LPHD1 = { + LogicalNodeModelType, + "LPHD1", + (ModelNode*) &iedModel_GenericIO, + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam, +}; + +DataObject iedModel_GenericIO_LPHD1_PhyNam = { + DataObjectModelType, + "PhyNam", + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam_vendor, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyNam, + NULL, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LPHD1_PhyHealth = { + DataObjectModelType, + "PhyHealth", + (ModelNode*) &iedModel_GenericIO_LPHD1, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LPHD1_PhyHealth, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_LPHD1_Proxy = { + DataObjectModelType, + "Proxy", + (ModelNode*) &iedModel_GenericIO_LPHD1, + NULL, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_LPHD1_Proxy_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_LPHD1_Proxy, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +LogicalNode iedModel_GenericIO_GGIO1 = { + LogicalNodeModelType, + "GGIO1", + (ModelNode*) &iedModel_GenericIO, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, +}; + +DataObject iedModel_GenericIO_GGIO1_Mod = { + DataObjectModelType, + "Mod", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod_ctlModel, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_Mod, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Beh = { + DataObjectModelType, + "Beh", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Beh_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Beh, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Health = { + DataObjectModelType, + "Health", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + (ModelNode*) &iedModel_GenericIO_GGIO1_Health_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Health_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Health, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_NamPlt = { + DataObjectModelType, + "NamPlt", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_vendor, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor = { + DataAttributeModelType, + "vendor", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_swRev, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev = { + DataAttributeModelType, + "swRev", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt_d, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d = { + DataAttributeModelType, + "d", + (ModelNode*) &iedModel_GenericIO_GGIO1_NamPlt, + NULL, + NULL, + 0, + -1, + IEC61850_FC_DC, + IEC61850_VISIBLE_STRING_255, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn1 = { + DataObjectModelType, + "AnIn1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn1, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn2 = { + DataObjectModelType, + "AnIn2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn2, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn3 = { + DataObjectModelType, + "AnIn3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn3, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_AnIn4 = { + DataObjectModelType, + "AnIn4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag = { + DataAttributeModelType, + "mag", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_q, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag_f, + 0, + -1, + IEC61850_FC_MX, + IEC61850_CONSTRUCTED, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f = { + DataAttributeModelType, + "f", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_mag, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_FLOAT32, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4_t, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_AnIn4, + NULL, + NULL, + 0, + -1, + IEC61850_FC_MX, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO1 = { + DataObjectModelType, + "SPCSO1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat, + 0, + -1, + IEC61850_FC_ST, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_stVal, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_ctlModel, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO1_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO2 = { + DataObjectModelType, + "SPCSO2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2_t, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO2, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO3 = { + DataObjectModelType, + "SPCSO3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3_t, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO3, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_SPCSO4 = { + DataObjectModelType, + "SPCSO4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper = { + DataAttributeModelType, + "Oper", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_ctlModel, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal = { + DataAttributeModelType, + "ctlVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin = { + DataAttributeModelType, + "origin", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CONSTRUCTED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat = { + DataAttributeModelType, + "orCat", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent = { + DataAttributeModelType, + "orIdent", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_OCTET_STRING_64, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum = { + DataAttributeModelType, + "ctlNum", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_T, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_INT8U, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T = { + DataAttributeModelType, + "T", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test = { + DataAttributeModelType, + "Test", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_BOOLEAN, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check = { + DataAttributeModelType, + "Check", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_Oper, + NULL, + NULL, + 0, + -1, + IEC61850_FC_CO, + IEC61850_CHECK, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel = { + DataAttributeModelType, + "ctlModel", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4_t, + NULL, + 0, + -1, + IEC61850_FC_CF, + IEC61850_ENUMERATED, + 0, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_SPCSO4, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind1 = { + DataObjectModelType, + "Ind1", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind1_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind1, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind2 = { + DataObjectModelType, + "Ind2", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind2_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind2, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind3 = { + DataObjectModelType, + "Ind3", + (ModelNode*) &iedModel_GenericIO_GGIO1, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind3_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind3, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +DataObject iedModel_GenericIO_GGIO1_Ind4 = { + DataObjectModelType, + "Ind4", + (ModelNode*) &iedModel_GenericIO_GGIO1, + NULL, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_stVal, + 0, + -1 +}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal = { + DataAttributeModelType, + "stVal", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_q, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_BOOLEAN, + 0 + TRG_OPT_DATA_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_q = { + DataAttributeModelType, + "q", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4_t, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_QUALITY, + 0 + TRG_OPT_QUALITY_CHANGED, + NULL, + 0}; + +DataAttribute iedModel_GenericIO_GGIO1_Ind4_t = { + DataAttributeModelType, + "t", + (ModelNode*) &iedModel_GenericIO_GGIO1_Ind4, + NULL, + NULL, + 0, + -1, + IEC61850_FC_ST, + IEC61850_TIMESTAMP, + 0, + NULL, + 0}; + +extern ReportControlBlock iedModel_GenericIO_LLN0_report0; +extern ReportControlBlock iedModel_GenericIO_LLN0_report1; +extern ReportControlBlock iedModel_GenericIO_LLN0_report2; +extern ReportControlBlock iedModel_GenericIO_LLN0_report3; +extern ReportControlBlock iedModel_GenericIO_LLN0_report4; +extern ReportControlBlock iedModel_GenericIO_LLN0_report5; +extern ReportControlBlock iedModel_GenericIO_LLN0_report6; +extern ReportControlBlock iedModel_GenericIO_LLN0_report7; +extern ReportControlBlock iedModel_GenericIO_LLN0_report8; +extern ReportControlBlock iedModel_GenericIO_LLN0_report9; + +ReportControlBlock iedModel_GenericIO_LLN0_report0 = {&iedModel_GenericIO_LLN0, "EventsRCB01", "Events1", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report1}; +ReportControlBlock iedModel_GenericIO_LLN0_report1 = {&iedModel_GenericIO_LLN0, "EventsRCBPreConf01", "Events1", false, "Events", 1, 88, 175, 50, 1000, {0x4, 0xc0, 0xa8, 0x2, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report2}; +ReportControlBlock iedModel_GenericIO_LLN0_report2 = {&iedModel_GenericIO_LLN0, "EventsBRCB01", "Events2", true, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report3}; +ReportControlBlock iedModel_GenericIO_LLN0_report3 = {&iedModel_GenericIO_LLN0, "EventsBRCBPreConf01", "Events2", true, "Events", 1, 88, 175, 50, 1000, {0x4, 0xc0, 0xa8, 0x2, 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report4}; +ReportControlBlock iedModel_GenericIO_LLN0_report4 = {&iedModel_GenericIO_LLN0, "EventsIndexed01", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report5}; +ReportControlBlock iedModel_GenericIO_LLN0_report5 = {&iedModel_GenericIO_LLN0, "EventsIndexed02", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report6}; +ReportControlBlock iedModel_GenericIO_LLN0_report6 = {&iedModel_GenericIO_LLN0, "EventsIndexed03", "Events2", false, "Events", 1, 88, 175, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report7}; +ReportControlBlock iedModel_GenericIO_LLN0_report7 = {&iedModel_GenericIO_LLN0, "Measurements01", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report8}; +ReportControlBlock iedModel_GenericIO_LLN0_report8 = {&iedModel_GenericIO_LLN0, "Measurements02", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, &iedModel_GenericIO_LLN0_report9}; +ReportControlBlock iedModel_GenericIO_LLN0_report9 = {&iedModel_GenericIO_LLN0, "Measurements03", "Measurements", true, "Measurements", 1, 80, 239, 50, 1000, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, NULL}; + + + + + + + +IedModel iedModel = { + "simpleIO", + &iedModel_GenericIO, + &iedModelds_GenericIO_LLN0_Events, + &iedModel_GenericIO_LLN0_report0, + NULL, + NULL, + NULL, + NULL, + NULL, + initializeValues +}; + +static void +initializeValues() +{ + +iedModel_GenericIO_LLN0_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_GenericIO_LLN0_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_LLN0_NamPlt_vendor.mmsValue = MmsValue_newVisibleString("MZ Automation"); + +iedModel_GenericIO_LLN0_NamPlt_swRev.mmsValue = MmsValue_newVisibleString("1.3.0"); + +iedModel_GenericIO_LLN0_NamPlt_d.mmsValue = MmsValue_newVisibleString("libiec61850 server example"); + +iedModel_GenericIO_LPHD1_PhyHealth_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Mod_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Mod_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(0); + +iedModel_GenericIO_GGIO1_Beh_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_Health_stVal.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO1_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO2_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO3_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); + +iedModel_GenericIO_GGIO1_SPCSO4_ctlModel.mmsValue = MmsValue_newIntegerFromInt32(1); +} diff --git a/examples/server_example_access_control/static_model.h b/examples/server_example_access_control/static_model.h new file mode 100644 index 00000000..b6030e51 --- /dev/null +++ b/examples/server_example_access_control/static_model.h @@ -0,0 +1,311 @@ +/* + * static_model.h + * + * automatically generated from simpleIO_direct_control.cid + */ + +#ifndef STATIC_MODEL_H_ +#define STATIC_MODEL_H_ + +#include +#include "iec61850_model.h" + +extern IedModel iedModel; +extern LogicalDevice iedModel_GenericIO; +extern LogicalNode iedModel_GenericIO_LLN0; +extern DataObject iedModel_GenericIO_LLN0_Mod; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_q; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_t; +extern DataAttribute iedModel_GenericIO_LLN0_Mod_ctlModel; +extern DataObject iedModel_GenericIO_LLN0_Beh; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_q; +extern DataAttribute iedModel_GenericIO_LLN0_Beh_t; +extern DataObject iedModel_GenericIO_LLN0_Health; +extern DataAttribute iedModel_GenericIO_LLN0_Health_stVal; +extern DataAttribute iedModel_GenericIO_LLN0_Health_q; +extern DataAttribute iedModel_GenericIO_LLN0_Health_t; +extern DataObject iedModel_GenericIO_LLN0_NamPlt; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_vendor; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_swRev; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_d; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_configRev; +extern DataAttribute iedModel_GenericIO_LLN0_NamPlt_ldNs; +extern LogicalNode iedModel_GenericIO_LPHD1; +extern DataObject iedModel_GenericIO_LPHD1_PhyNam; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyNam_vendor; +extern DataObject iedModel_GenericIO_LPHD1_PhyHealth; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_stVal; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_q; +extern DataAttribute iedModel_GenericIO_LPHD1_PhyHealth_t; +extern DataObject iedModel_GenericIO_LPHD1_Proxy; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_stVal; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_q; +extern DataAttribute iedModel_GenericIO_LPHD1_Proxy_t; +extern LogicalNode iedModel_GenericIO_GGIO1; +extern DataObject iedModel_GenericIO_GGIO1_Mod; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_t; +extern DataAttribute iedModel_GenericIO_GGIO1_Mod_ctlModel; +extern DataObject iedModel_GenericIO_GGIO1_Beh; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Beh_t; +extern DataObject iedModel_GenericIO_GGIO1_Health; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Health_t; +extern DataObject iedModel_GenericIO_GGIO1_NamPlt; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_vendor; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_swRev; +extern DataAttribute iedModel_GenericIO_GGIO1_NamPlt_d; +extern DataObject iedModel_GenericIO_GGIO1_AnIn1; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn1_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn2; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn2_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn3; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn3_t; +extern DataObject iedModel_GenericIO_GGIO1_AnIn4; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_mag_f; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_AnIn4_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO1; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_t; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO2; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO2_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO3; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO3_t; +extern DataObject iedModel_GenericIO_GGIO1_SPCSO4; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_T; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_ctlModel; +extern DataAttribute iedModel_GenericIO_GGIO1_SPCSO4_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind1; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind1_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind2; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind2_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind3; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind3_t; +extern DataObject iedModel_GenericIO_GGIO1_Ind4; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_stVal; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_q; +extern DataAttribute iedModel_GenericIO_GGIO1_Ind4_t; + + + +#define IEDMODEL_GenericIO (&iedModel_GenericIO) +#define IEDMODEL_GenericIO_LLN0 (&iedModel_GenericIO_LLN0) +#define IEDMODEL_GenericIO_LLN0_Mod (&iedModel_GenericIO_LLN0_Mod) +#define IEDMODEL_GenericIO_LLN0_Mod_stVal (&iedModel_GenericIO_LLN0_Mod_stVal) +#define IEDMODEL_GenericIO_LLN0_Mod_q (&iedModel_GenericIO_LLN0_Mod_q) +#define IEDMODEL_GenericIO_LLN0_Mod_t (&iedModel_GenericIO_LLN0_Mod_t) +#define IEDMODEL_GenericIO_LLN0_Mod_ctlModel (&iedModel_GenericIO_LLN0_Mod_ctlModel) +#define IEDMODEL_GenericIO_LLN0_Beh (&iedModel_GenericIO_LLN0_Beh) +#define IEDMODEL_GenericIO_LLN0_Beh_stVal (&iedModel_GenericIO_LLN0_Beh_stVal) +#define IEDMODEL_GenericIO_LLN0_Beh_q (&iedModel_GenericIO_LLN0_Beh_q) +#define IEDMODEL_GenericIO_LLN0_Beh_t (&iedModel_GenericIO_LLN0_Beh_t) +#define IEDMODEL_GenericIO_LLN0_Health (&iedModel_GenericIO_LLN0_Health) +#define IEDMODEL_GenericIO_LLN0_Health_stVal (&iedModel_GenericIO_LLN0_Health_stVal) +#define IEDMODEL_GenericIO_LLN0_Health_q (&iedModel_GenericIO_LLN0_Health_q) +#define IEDMODEL_GenericIO_LLN0_Health_t (&iedModel_GenericIO_LLN0_Health_t) +#define IEDMODEL_GenericIO_LLN0_NamPlt (&iedModel_GenericIO_LLN0_NamPlt) +#define IEDMODEL_GenericIO_LLN0_NamPlt_vendor (&iedModel_GenericIO_LLN0_NamPlt_vendor) +#define IEDMODEL_GenericIO_LLN0_NamPlt_swRev (&iedModel_GenericIO_LLN0_NamPlt_swRev) +#define IEDMODEL_GenericIO_LLN0_NamPlt_d (&iedModel_GenericIO_LLN0_NamPlt_d) +#define IEDMODEL_GenericIO_LLN0_NamPlt_configRev (&iedModel_GenericIO_LLN0_NamPlt_configRev) +#define IEDMODEL_GenericIO_LLN0_NamPlt_ldNs (&iedModel_GenericIO_LLN0_NamPlt_ldNs) +#define IEDMODEL_GenericIO_LPHD1 (&iedModel_GenericIO_LPHD1) +#define IEDMODEL_GenericIO_LPHD1_PhyNam (&iedModel_GenericIO_LPHD1_PhyNam) +#define IEDMODEL_GenericIO_LPHD1_PhyNam_vendor (&iedModel_GenericIO_LPHD1_PhyNam_vendor) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth (&iedModel_GenericIO_LPHD1_PhyHealth) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_stVal (&iedModel_GenericIO_LPHD1_PhyHealth_stVal) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_q (&iedModel_GenericIO_LPHD1_PhyHealth_q) +#define IEDMODEL_GenericIO_LPHD1_PhyHealth_t (&iedModel_GenericIO_LPHD1_PhyHealth_t) +#define IEDMODEL_GenericIO_LPHD1_Proxy (&iedModel_GenericIO_LPHD1_Proxy) +#define IEDMODEL_GenericIO_LPHD1_Proxy_stVal (&iedModel_GenericIO_LPHD1_Proxy_stVal) +#define IEDMODEL_GenericIO_LPHD1_Proxy_q (&iedModel_GenericIO_LPHD1_Proxy_q) +#define IEDMODEL_GenericIO_LPHD1_Proxy_t (&iedModel_GenericIO_LPHD1_Proxy_t) +#define IEDMODEL_GenericIO_GGIO1 (&iedModel_GenericIO_GGIO1) +#define IEDMODEL_GenericIO_GGIO1_Mod (&iedModel_GenericIO_GGIO1_Mod) +#define IEDMODEL_GenericIO_GGIO1_Mod_stVal (&iedModel_GenericIO_GGIO1_Mod_stVal) +#define IEDMODEL_GenericIO_GGIO1_Mod_q (&iedModel_GenericIO_GGIO1_Mod_q) +#define IEDMODEL_GenericIO_GGIO1_Mod_t (&iedModel_GenericIO_GGIO1_Mod_t) +#define IEDMODEL_GenericIO_GGIO1_Mod_ctlModel (&iedModel_GenericIO_GGIO1_Mod_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_Beh (&iedModel_GenericIO_GGIO1_Beh) +#define IEDMODEL_GenericIO_GGIO1_Beh_stVal (&iedModel_GenericIO_GGIO1_Beh_stVal) +#define IEDMODEL_GenericIO_GGIO1_Beh_q (&iedModel_GenericIO_GGIO1_Beh_q) +#define IEDMODEL_GenericIO_GGIO1_Beh_t (&iedModel_GenericIO_GGIO1_Beh_t) +#define IEDMODEL_GenericIO_GGIO1_Health (&iedModel_GenericIO_GGIO1_Health) +#define IEDMODEL_GenericIO_GGIO1_Health_stVal (&iedModel_GenericIO_GGIO1_Health_stVal) +#define IEDMODEL_GenericIO_GGIO1_Health_q (&iedModel_GenericIO_GGIO1_Health_q) +#define IEDMODEL_GenericIO_GGIO1_Health_t (&iedModel_GenericIO_GGIO1_Health_t) +#define IEDMODEL_GenericIO_GGIO1_NamPlt (&iedModel_GenericIO_GGIO1_NamPlt) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_vendor (&iedModel_GenericIO_GGIO1_NamPlt_vendor) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_swRev (&iedModel_GenericIO_GGIO1_NamPlt_swRev) +#define IEDMODEL_GenericIO_GGIO1_NamPlt_d (&iedModel_GenericIO_GGIO1_NamPlt_d) +#define IEDMODEL_GenericIO_GGIO1_AnIn1 (&iedModel_GenericIO_GGIO1_AnIn1) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag (&iedModel_GenericIO_GGIO1_AnIn1_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_mag_f (&iedModel_GenericIO_GGIO1_AnIn1_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_q (&iedModel_GenericIO_GGIO1_AnIn1_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn1_t (&iedModel_GenericIO_GGIO1_AnIn1_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn2 (&iedModel_GenericIO_GGIO1_AnIn2) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag (&iedModel_GenericIO_GGIO1_AnIn2_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_mag_f (&iedModel_GenericIO_GGIO1_AnIn2_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_q (&iedModel_GenericIO_GGIO1_AnIn2_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn2_t (&iedModel_GenericIO_GGIO1_AnIn2_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn3 (&iedModel_GenericIO_GGIO1_AnIn3) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag (&iedModel_GenericIO_GGIO1_AnIn3_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_mag_f (&iedModel_GenericIO_GGIO1_AnIn3_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_q (&iedModel_GenericIO_GGIO1_AnIn3_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn3_t (&iedModel_GenericIO_GGIO1_AnIn3_t) +#define IEDMODEL_GenericIO_GGIO1_AnIn4 (&iedModel_GenericIO_GGIO1_AnIn4) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag (&iedModel_GenericIO_GGIO1_AnIn4_mag) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_mag_f (&iedModel_GenericIO_GGIO1_AnIn4_mag_f) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_q (&iedModel_GenericIO_GGIO1_AnIn4_q) +#define IEDMODEL_GenericIO_GGIO1_AnIn4_t (&iedModel_GenericIO_GGIO1_AnIn4_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1 (&iedModel_GenericIO_GGIO1_SPCSO1) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin (&iedModel_GenericIO_GGIO1_SPCSO1_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_stVal (&iedModel_GenericIO_GGIO1_SPCSO1_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_q (&iedModel_GenericIO_GGIO1_SPCSO1_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_t (&iedModel_GenericIO_GGIO1_SPCSO1_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO1_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper (&iedModel_GenericIO_GGIO1_SPCSO1_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO1_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO1_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2 (&iedModel_GenericIO_GGIO1_SPCSO2) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_stVal (&iedModel_GenericIO_GGIO1_SPCSO2_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_q (&iedModel_GenericIO_GGIO1_SPCSO2_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper (&iedModel_GenericIO_GGIO1_SPCSO2_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO2_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO2_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO2_t (&iedModel_GenericIO_GGIO1_SPCSO2_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3 (&iedModel_GenericIO_GGIO1_SPCSO3) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_stVal (&iedModel_GenericIO_GGIO1_SPCSO3_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_q (&iedModel_GenericIO_GGIO1_SPCSO3_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper (&iedModel_GenericIO_GGIO1_SPCSO3_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO3_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO3_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO3_t (&iedModel_GenericIO_GGIO1_SPCSO3_t) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4 (&iedModel_GenericIO_GGIO1_SPCSO4) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_stVal (&iedModel_GenericIO_GGIO1_SPCSO4_stVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_q (&iedModel_GenericIO_GGIO1_SPCSO4_q) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper (&iedModel_GenericIO_GGIO1_SPCSO4_Oper) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlVal (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlVal) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orCat) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_origin_orIdent) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_ctlNum (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_ctlNum) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_T (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_T) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Test (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Test) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_Oper_Check (&iedModel_GenericIO_GGIO1_SPCSO4_Oper_Check) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_ctlModel (&iedModel_GenericIO_GGIO1_SPCSO4_ctlModel) +#define IEDMODEL_GenericIO_GGIO1_SPCSO4_t (&iedModel_GenericIO_GGIO1_SPCSO4_t) +#define IEDMODEL_GenericIO_GGIO1_Ind1 (&iedModel_GenericIO_GGIO1_Ind1) +#define IEDMODEL_GenericIO_GGIO1_Ind1_stVal (&iedModel_GenericIO_GGIO1_Ind1_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind1_q (&iedModel_GenericIO_GGIO1_Ind1_q) +#define IEDMODEL_GenericIO_GGIO1_Ind1_t (&iedModel_GenericIO_GGIO1_Ind1_t) +#define IEDMODEL_GenericIO_GGIO1_Ind2 (&iedModel_GenericIO_GGIO1_Ind2) +#define IEDMODEL_GenericIO_GGIO1_Ind2_stVal (&iedModel_GenericIO_GGIO1_Ind2_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind2_q (&iedModel_GenericIO_GGIO1_Ind2_q) +#define IEDMODEL_GenericIO_GGIO1_Ind2_t (&iedModel_GenericIO_GGIO1_Ind2_t) +#define IEDMODEL_GenericIO_GGIO1_Ind3 (&iedModel_GenericIO_GGIO1_Ind3) +#define IEDMODEL_GenericIO_GGIO1_Ind3_stVal (&iedModel_GenericIO_GGIO1_Ind3_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind3_q (&iedModel_GenericIO_GGIO1_Ind3_q) +#define IEDMODEL_GenericIO_GGIO1_Ind3_t (&iedModel_GenericIO_GGIO1_Ind3_t) +#define IEDMODEL_GenericIO_GGIO1_Ind4 (&iedModel_GenericIO_GGIO1_Ind4) +#define IEDMODEL_GenericIO_GGIO1_Ind4_stVal (&iedModel_GenericIO_GGIO1_Ind4_stVal) +#define IEDMODEL_GenericIO_GGIO1_Ind4_q (&iedModel_GenericIO_GGIO1_Ind4_q) +#define IEDMODEL_GenericIO_GGIO1_Ind4_t (&iedModel_GenericIO_GGIO1_Ind4_t) + +#endif /* STATIC_MODEL_H_ */ + diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 3e896495..c22311f6 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -334,6 +334,9 @@ struct sMmsMapping { IedServer_RCBEventHandler rcbEventHandler; void* rcbEventHandlerParameter; + + IedServer_DataSetAccessHandler dataSetAccessHandler; + void* dataSetAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index c543a41d..d748bf5a 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1917,3 +1917,10 @@ IedServer_ignoreClientRequests(IedServer self, bool enable) MmsServer_ignoreClientRequests(self->mmsServer, enable); } } + +void +IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter) +{ + self->mmsMapping->dataSetAccessHandler = handler; + self->mmsMapping->dataSetAccessHandlerParameter = parameter; +} diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 4d6d8b31..a33e31b2 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3378,13 +3378,46 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS return DATA_ACCESS_ERROR_SUCCESS; } +static bool +checkDataSetAccess(MmsMapping* self, MmsServerConnection connection, MmsVariableListType listType, MmsDomain* domain, char* listName, IedServer_DataSetOperation operation) +{ + bool accessGranted = true; + + if (self->dataSetAccessHandler) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + char dataSetRef[130]; + dataSetRef[0] = 0; + + if (listType == MMS_ASSOCIATION_SPECIFIC) { + dataSetRef[0] = '@'; + StringUtils_copyStringToBuffer(dataSetRef + 1, listName); + } + else if (listType == MMS_VMD_SPECIFIC) { + StringUtils_copyStringToBuffer(dataSetRef, listName); + } + else if (listType == MMS_DOMAIN_SPECIFIC) { + StringUtils_appendString(dataSetRef, 129, domain->domainName); + StringUtils_appendString(dataSetRef, 129, "/"); + StringUtils_appendString(dataSetRef, 129, listName); + } + + accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, + private_IedServer_getClientConnectionByHandle(self->iedServer, connection), + operation, dataSetRef); + } + + return accessGranted; +} + static MmsError variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, char* listName, MmsServerConnection connection) { MmsError allow = MMS_ERROR_NONE; - (void)connection; + MmsMapping* self = (MmsMapping*) parameter; /* TODO add log message */ @@ -3415,130 +3448,142 @@ variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType printf("specific (name=%s)\n", listName); #endif /* (DEBUG_IED_SERVER == 1) */ - MmsMapping* self = (MmsMapping*) parameter; - if (accessType == MMS_VARLIST_CREATE) { - //TODO call user callback hander (IedServer_DataSetAccessHandler) + 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) */ + if (listType == MMS_DOMAIN_SPECIFIC) { + /* check if LN exists - otherwise reject request (to fulfill test case sDsN1c) */ - allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; + allow = MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT; - IedModel* model = self->model; + IedModel* model = self->model; - LogicalDevice* ld = IedModel_getDevice(model, domain->domainName); + LogicalDevice* ld = IedModel_getDevice(model, domain->domainName); - if (ld != NULL) { + if (ld != NULL) { - char lnName[129]; + char lnName[129]; - char* separator = strchr(listName, '$'); + char* separator = strchr(listName, '$'); - if (separator != NULL) { - int lnNameLen = separator - listName; + if (separator != NULL) { + int lnNameLen = separator - listName; - memcpy(lnName, listName, lnNameLen); - lnName[lnNameLen] = 0; + memcpy(lnName, listName, lnNameLen); + lnName[lnNameLen] = 0; - if (LogicalDevice_getLogicalNode(ld, lnName) != NULL) - allow = MMS_ERROR_NONE; - } + if (LogicalDevice_getLogicalNode(ld, lnName) != NULL) + allow = MMS_ERROR_NONE; + } + } } - + } + else { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; } } else if (accessType == MMS_VARLIST_DELETE) { - //TODO call user callback hander (IedServer_DataSetAccessHandler) + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_DELETE)) { + /* Check if data set is referenced in a report */ - /* Check if data set is referenced in a report */ + LinkedList rcElement = self->reportControls; - LinkedList rcElement = self->reportControls; + while ((rcElement = LinkedList_getNext(rcElement)) != NULL) { + ReportControl* rc = (ReportControl*) rcElement->data; - while ((rcElement = LinkedList_getNext(rcElement)) != NULL) { - ReportControl* rc = (ReportControl*) rcElement->data; + if (rc->isDynamicDataSet) { + if (rc->dataSet != NULL) { - if (rc->isDynamicDataSet) { - if (rc->dataSet != NULL) { - - if (listType == MMS_DOMAIN_SPECIFIC) { - if (rc->dataSet->logicalDeviceName != NULL) { - if (strcmp(rc->dataSet->name, listName) == 0) { - if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + if (listType == MMS_DOMAIN_SPECIFIC) { + if (rc->dataSet->logicalDeviceName != NULL) { + if (strcmp(rc->dataSet->name, listName) == 0) { + if (strcmp(rc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } } - } - else if (listType == MMS_VMD_SPECIFIC) { - if (rc->dataSet->logicalDeviceName == NULL) { - if (strcmp(rc->dataSet->name, listName) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + else if (listType == MMS_VMD_SPECIFIC) { + if (rc->dataSet->logicalDeviceName == NULL) { + if (strcmp(rc->dataSet->name, listName) == 0) { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } - } - else if (listType == MMS_ASSOCIATION_SPECIFIC) { - if (rc->dataSet->logicalDeviceName == NULL) { - if (strcmp(rc->dataSet->name, listName) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + else if (listType == MMS_ASSOCIATION_SPECIFIC) { + if (rc->dataSet->logicalDeviceName == NULL) { + if (strcmp(rc->dataSet->name, listName) == 0) { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } - } + } } } - } - #if (CONFIG_IEC61850_LOG_SERVICE == 1) - /* check if data set is referenced in a log control block*/ - LinkedList logElement = self->logControls; + /* check if data set is referenced in a log control block*/ + LinkedList logElement = self->logControls; - while ((logElement = LinkedList_getNext(logElement)) != NULL) { - LogControl* lc = (LogControl*) logElement->data; + while ((logElement = LinkedList_getNext(logElement)) != NULL) { + LogControl* lc = (LogControl*) logElement->data; - if (lc->isDynamicDataSet) { - if (lc->dataSet != NULL) { + if (lc->isDynamicDataSet) { + if (lc->dataSet != NULL) { - if (listType == MMS_DOMAIN_SPECIFIC) { - if (lc->dataSet->logicalDeviceName != NULL) { - if (strcmp(lc->dataSet->name, listName) == 0) { - if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + if (listType == MMS_DOMAIN_SPECIFIC) { + if (lc->dataSet->logicalDeviceName != NULL) { + if (strcmp(lc->dataSet->name, listName) == 0) { + if (strcmp(lc->dataSet->logicalDeviceName, MmsDomain_getName(domain) + strlen(self->model->name)) == 0) { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } } - } - else if (listType == MMS_VMD_SPECIFIC) { - if (lc->dataSet->logicalDeviceName == NULL) { - if (strcmp(lc->dataSet->name, listName) == 0) { - allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; - break; + else if (listType == MMS_VMD_SPECIFIC) { + if (lc->dataSet->logicalDeviceName == NULL) { + if (strcmp(lc->dataSet->name, listName) == 0) { + allow = MMS_ERROR_SERVICE_OBJECT_CONSTRAINT_CONFLICT; + break; + } } } - } + } } } - } #endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ + + } + else { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } } - else if (accessType == MMS_VARLIST_READ) { - //TODO call user callback hander (IedServer_DataSetAccessHandler) + 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) { - //TODO call user callback hander (IedServer_DataSetAccessHandler) + 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) { - //TODO call user callback hander (IedServer_DataSetAccessHandler) + if (checkDataSetAccess(self, connection, listType, domain, listName, DATASET_GET_DIRECTORY) == false) { + allow = MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED; + } } return allow; diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h index cdd04bcb..e3af848f 100644 --- a/src/mms/inc/iso_connection_parameters.h +++ b/src/mms/inc/iso_connection_parameters.h @@ -1,7 +1,7 @@ /* * iso_connection_parameters.h * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -24,6 +24,10 @@ #ifndef ISO_CONNECTION_PARAMETERS_H_ #define ISO_CONNECTION_PARAMETERS_H_ +#ifndef CONFIG_MMS_SUPPORT_TLS +#define CONFIG_MMS_SUPPORT_TLS 0 +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/src/mms/iso_mms/server/mms_named_variable_list_service.c b/src/mms/iso_mms/server/mms_named_variable_list_service.c index 4baf4ab3..85f1b960 100644 --- a/src/mms/iso_mms/server/mms_named_variable_list_service.c +++ b/src/mms/iso_mms/server/mms_named_variable_list_service.c @@ -50,66 +50,66 @@ MmsError mmsServer_callVariableListChangedHandler(MmsVariableListAccessType accessType, MmsVariableListType listType, MmsDomain* domain, - char* listName, MmsServerConnection connection) + char* listName, MmsServerConnection connection) { - MmsServer self = connection->server; + MmsServer self = connection->server; - if (self->variableListAccessHandler != NULL) { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: call MmsNamedVariableListAccessHandler for new list %s\n", listName); + if (self->variableListAccessHandler != NULL) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: call MmsNamedVariableListAccessHandler for new list %s\n", listName); - return self->variableListAccessHandler(self->variableListAccessHandlerParameter, - accessType, listType, domain, listName, connection); - } - else - return MMS_ERROR_NONE; + return self->variableListAccessHandler(self->variableListAccessHandlerParameter, + accessType, listType, domain, listName, connection); + } + else + return MMS_ERROR_NONE; } static void createDeleteNamedVariableListResponse(uint32_t invokeId, ByteBuffer* response, - uint32_t numberMatched, uint32_t numberDeleted) + uint32_t numberMatched, uint32_t numberDeleted) { - uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2; + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize(invokeId) + 2; - uint32_t numberMatchedSize = - 2 + BerEncoder_UInt32determineEncodedSize(numberMatched); + uint32_t numberMatchedSize = + 2 + BerEncoder_UInt32determineEncodedSize(numberMatched); - uint32_t numberDeletedSize = - 2 + BerEncoder_UInt32determineEncodedSize(numberDeleted); + uint32_t numberDeletedSize = + 2 + BerEncoder_UInt32determineEncodedSize(numberDeleted); - uint32_t deleteNVLSize = 2 + numberMatchedSize + numberDeletedSize; + uint32_t deleteNVLSize = 2 + numberMatchedSize + numberDeletedSize; - uint32_t confirmedResponsePDUSize = invokeIdSize + deleteNVLSize; + uint32_t confirmedResponsePDUSize = invokeIdSize + deleteNVLSize; - int bufPos = 0; - uint8_t* buffer = response->buffer; + int bufPos = 0; + uint8_t* buffer = response->buffer; - bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0xad, numberMatchedSize + numberDeletedSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xad, numberMatchedSize + numberDeletedSize, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x80, numberMatchedSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(numberMatched, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x80, numberMatchedSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(numberMatched, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x81, numberDeletedSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(numberDeleted, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x81, numberDeletedSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(numberDeleted, buffer, bufPos); - response->size = bufPos; + response->size = bufPos; } static void /* Confirmed service error (ServiceError) */ createServiceErrorDeleteVariableLists(uint32_t invokeId, ByteBuffer* response, - MmsError errorType, uint32_t numberDeleted) + MmsError errorType, uint32_t numberDeleted) { - uint8_t buffer[8]; + uint8_t buffer[8]; - int size = BerEncoder_encodeUInt32WithTL(0x86, numberDeleted, buffer, 0); + int size = BerEncoder_encodeUInt32WithTL(0x86, numberDeleted, buffer, 0); - mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType, - buffer, size); + mmsServer_createServiceErrorPduWithServiceSpecificInfo(invokeId, response, errorType, + buffer, size); } void @@ -118,38 +118,38 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response) { - (void)bufPos; - - DeleteNamedVariableListRequest_t* request = NULL; - MmsPdu_t* mmsPdu = NULL; - - asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); - - if (rval.code != RC_OK) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_function; - } - - if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && - (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present - == ConfirmedServiceRequest_PR_deleteNamedVariableList)) - { - request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); - } - else { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_function; - } + (void)bufPos; + + DeleteNamedVariableListRequest_t* request = NULL; + MmsPdu_t* mmsPdu = NULL; + + asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); + + if (rval.code != RC_OK) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; + } + + if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && + (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present + == ConfirmedServiceRequest_PR_deleteNamedVariableList)) + { + request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.deleteNamedVariableList); + } + else { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; + } long scopeOfDelete = DeleteNamedVariableListRequest__scopeOfDelete_specific; if (request->scopeOfDelete) - asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete); + asn_INTEGER2long(request->scopeOfDelete, &scopeOfDelete); MmsDevice* device = MmsServer_getDevice(connection->server); if (scopeOfDelete == DeleteNamedVariableListRequest__scopeOfDelete_specific) { - MmsError serviceError = MMS_ERROR_NONE; + MmsError serviceError = MMS_ERROR_NONE; int numberMatched = 0; int numberDeleted = 0; @@ -160,43 +160,43 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, for (i = 0; i < numberItems; i++) { if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_domainspecific) { - char domainName[65]; - char listName[65]; + char domainName[65]; + char listName[65]; - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.domainId, - domainName, 65); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.domainId, + domainName, 65); - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.itemId, - listName, 65); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.domainspecific.itemId, + listName, 65); - MmsDomain* domain = MmsDevice_getDomain(device, domainName); + MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { + if (domain != NULL) { - MmsNamedVariableList variableList = MmsDomain_getNamedVariableList(domain, listName); + MmsNamedVariableList variableList = MmsDomain_getNamedVariableList(domain, listName); - if (variableList != NULL) { - numberMatched++; + if (variableList != NULL) { + numberMatched++; - if (MmsNamedVariableList_isDeletable(variableList)) { + if (MmsNamedVariableList_isDeletable(variableList)) { - MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_DOMAIN_SPECIFIC, domain, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_DOMAIN_SPECIFIC, domain, listName, connection); - if (deleteError == MMS_ERROR_NONE) { - MmsDomain_deleteNamedVariableList(domain, listName); - numberDeleted++; - } - else - serviceError = deleteError; - } - } - } + if (deleteError == MMS_ERROR_NONE) { + MmsDomain_deleteNamedVariableList(domain, listName); + numberDeleted++; + } + else + serviceError = deleteError; + } + } + } } else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_aaspecific) { - char listName[65]; + char listName[65]; - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.aaspecific, - listName, 65); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.aaspecific, + listName, 65); MmsNamedVariableList variableList = MmsServerConnection_getNamedVariableList(connection, listName); @@ -206,40 +206,40 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_ASSOCIATION_SPECIFIC, NULL, listName, connection); if (deleteError == MMS_ERROR_NONE) { - numberDeleted++; - MmsServerConnection_deleteNamedVariableList(connection, listName); + numberDeleted++; + MmsServerConnection_deleteNamedVariableList(connection, listName); } else - serviceError = deleteError; + serviceError = deleteError; } } else if (request->listOfVariableListName->list.array[i]->present == ObjectName_PR_vmdspecific) { - char listName[65]; + char listName[65]; - mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.vmdspecific, - listName, 65); + mmsMsg_copyAsn1IdentifierToStringBuffer(request->listOfVariableListName->list.array[i]->choice.vmdspecific, + listName, 65); - MmsNamedVariableList variableList = mmsServer_getNamedVariableListWithName(device->namedVariableLists, listName); + MmsNamedVariableList variableList = mmsServer_getNamedVariableListWithName(device->namedVariableLists, listName); - if (variableList != NULL) { - numberMatched++; + if (variableList != NULL) { + numberMatched++; - MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_VMD_SPECIFIC, NULL, listName, connection); + MmsError deleteError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_DELETE, MMS_VMD_SPECIFIC, NULL, listName, connection); - if (deleteError == MMS_ERROR_NONE) { - numberDeleted++; - mmsServer_deleteVariableList(device->namedVariableLists, listName); - } - else - serviceError = deleteError; - } + if (deleteError == MMS_ERROR_NONE) { + numberDeleted++; + mmsServer_deleteVariableList(device->namedVariableLists, listName); + } + else + serviceError = deleteError; + } } } if (serviceError == MMS_ERROR_NONE) - createDeleteNamedVariableListResponse(invokeId, response, numberMatched, numberDeleted); + createDeleteNamedVariableListResponse(invokeId, response, numberMatched, numberDeleted); else - createServiceErrorDeleteVariableLists(invokeId, response, serviceError, numberDeleted); + createServiceErrorDeleteVariableLists(invokeId, response, serviceError, numberDeleted); } else { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); @@ -247,61 +247,61 @@ mmsServer_handleDeleteNamedVariableListRequest(MmsServerConnection connection, exit_function: - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return; + return; } static void createDefineNamedVariableListResponse(uint32_t invokeId, ByteBuffer* response) { - uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2; + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2; - uint32_t confirmedResponsePDUSize = 2 + invokeIdSize; + uint32_t confirmedResponsePDUSize = 2 + invokeIdSize; - int bufPos = 0; - uint8_t* buffer = response->buffer; + int bufPos = 0; + uint8_t* buffer = response->buffer; - bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); - bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32(invokeId, buffer, bufPos); - bufPos = BerEncoder_encodeTL(0x8b, 0, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0x8b, 0, buffer, bufPos); - response->size = bufPos; + response->size = bufPos; } static bool checkIfVariableExists(MmsDevice* device, MmsAccessSpecifier* accessSpecifier) { - (void)device; + (void)device; - if (accessSpecifier->domain == NULL) - return false; + if (accessSpecifier->domain == NULL) + return false; - MmsVariableSpecification* variableSpec = - MmsDomain_getNamedVariable(accessSpecifier->domain, accessSpecifier->variableName); + MmsVariableSpecification* variableSpec = + MmsDomain_getNamedVariable(accessSpecifier->domain, accessSpecifier->variableName); - if (variableSpec == NULL) - return false; + if (variableSpec == NULL) + return false; - if (accessSpecifier->arrayIndex != -1) { - if (variableSpec->type != MMS_ARRAY) - return false; + if (accessSpecifier->arrayIndex != -1) { + if (variableSpec->type != MMS_ARRAY) + return false; - if (accessSpecifier->arrayIndex >= variableSpec->typeSpec.array.elementCount) - return false; + if (accessSpecifier->arrayIndex >= variableSpec->typeSpec.array.elementCount) + return false; - if (accessSpecifier->componentName != NULL) { - variableSpec = variableSpec->typeSpec.array.elementTypeSpec; + if (accessSpecifier->componentName != NULL) { + variableSpec = variableSpec->typeSpec.array.elementTypeSpec; - if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL) - return false; - } - } + if (MmsVariableSpecification_getNamedVariableRecursive(variableSpec, accessSpecifier->componentName) == NULL) + return false; + } + } - return true; + return true; } static MmsNamedVariableList @@ -309,20 +309,20 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, DefineNamedVariableListRequest_t* request, char* variableListName, MmsError* mmsError) { - MmsNamedVariableList namedVariableList = NULL; + MmsNamedVariableList namedVariableList = NULL; int variableCount = request->listOfVariable.list.count; if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries); + printf("MMS_SERVER: create-named-variable-list (%i variable(s) | max=%i)\n", variableCount, server->maxDataSetEntries); #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) if ((variableCount == 0 ) || (variableCount > server->maxDataSetEntries)) { #else if ((variableCount == 0 ) || (variableCount > CONFIG_MMS_MAX_NUMBER_OF_DATA_SET_MEMBERS)) { #endif - *mmsError = MMS_ERROR_DEFINITION_OTHER; - goto exit_function; + *mmsError = MMS_ERROR_DEFINITION_OTHER; + goto exit_function; } namedVariableList = MmsNamedVariableList_create(domain, variableListName, true); @@ -342,8 +342,8 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, if (request->listOfVariable.list.array[i]->alternateAccess->list.count != 1) { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n"); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable list - only one alternate access specification allowed!\n"); MmsNamedVariableList_destroy(namedVariableList); namedVariableList = NULL; @@ -355,31 +355,31 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, request->listOfVariable.list.array[i]->alternateAccess->list.array[0]; if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed) - && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) - && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == - AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index)) + && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAlternateAccess) + && (alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.present == + AlternateAccessSelection__selectAlternateAccess__accessSelection_PR_index)) { asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAlternateAccess.accessSelection.choice.index), &arrayIndex); if (alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess) { - componentNameBuf[0] = 0; + componentNameBuf[0] = 0; - componentName = mmsMsg_getComponentNameFromAlternateAccess( - alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess, - componentNameBuf, 0); + componentName = mmsMsg_getComponentNameFromAlternateAccess( + alternateAccess->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + componentNameBuf, 0); + } + else { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n"); } - else { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: create-named-variable-list - component specification is missing!\n"); - } } else if ((alternateAccess->present == AlternateAccess__Member_PR_unnamed) - && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) - && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index) - ) + && (alternateAccess->choice.unnamed->present == AlternateAccessSelection_PR_selectAccess) + && (alternateAccess->choice.unnamed->choice.selectAccess.present == AlternateAccessSelection__selectAccess_PR_index) + ) { - asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex); + asn_INTEGER2long(&(alternateAccess->choice.unnamed->choice.selectAccess.choice.index), &arrayIndex); } else { MmsNamedVariableList_destroy(namedVariableList); @@ -392,16 +392,16 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, if (varSpec->present == VariableSpecification_PR_name) { - char variableName[65]; - char domainId[65]; + char variableName[65]; + char domainId[65]; - StringUtils_createStringFromBufferInBuffer(variableName, - varSpec->choice.name.choice.domainspecific.itemId.buf, - varSpec->choice.name.choice.domainspecific.itemId.size); + StringUtils_createStringFromBufferInBuffer(variableName, + varSpec->choice.name.choice.domainspecific.itemId.buf, + varSpec->choice.name.choice.domainspecific.itemId.size); - StringUtils_createStringFromBufferInBuffer(domainId, - varSpec->choice.name.choice.domainspecific.domainId.buf, - varSpec->choice.name.choice.domainspecific.domainId.size); + StringUtils_createStringFromBufferInBuffer(domainId, + varSpec->choice.name.choice.domainspecific.domainId.buf, + varSpec->choice.name.choice.domainspecific.domainId.size); MmsDomain* elementDomain = MmsDevice_getDomain(device, domainId); @@ -413,24 +413,24 @@ createNamedVariableList(MmsServer server, MmsDomain* domain, MmsDevice* device, accessSpecifier.componentName = componentName; if (DEBUG_MMS_SERVER) - printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName); + printf("MMS SERVER: add named variable list entry: %s/%s(%li)%s\n", MmsDomain_getName(elementDomain), variableName, arrayIndex, componentName); /* check if element exists */ if (checkIfVariableExists(device, &accessSpecifier) == true) { - MmsNamedVariableListEntry variable = - MmsNamedVariableListEntry_create(accessSpecifier); + MmsNamedVariableListEntry variable = + MmsNamedVariableListEntry_create(accessSpecifier); - MmsNamedVariableList_addVariable(namedVariableList, variable); + MmsNamedVariableList_addVariable(namedVariableList, variable); } else { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: failed - variable does not exist!\n"); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: failed - variable does not exist!\n"); - MmsNamedVariableList_destroy(namedVariableList); - namedVariableList = NULL; - i = variableCount; /* exit loop after freeing loop variables */ - *mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED; + MmsNamedVariableList_destroy(namedVariableList); + namedVariableList = NULL; + i = variableCount; /* exit loop after freeing loop variables */ + *mmsError = MMS_ERROR_DEFINITION_OBJECT_UNDEFINED; } } else { @@ -453,7 +453,7 @@ mmsServer_handleDefineNamedVariableListRequest( uint32_t invokeId, ByteBuffer* response) { - (void)bufPos; + (void)bufPos; DefineNamedVariableListRequest_t* request = 0; @@ -462,177 +462,177 @@ mmsServer_handleDefineNamedVariableListRequest( asn_dec_rval_t rval = ber_decode(NULL, &asn_DEF_MmsPdu, (void**) &mmsPdu, buffer, maxBufPos); if (rval.code != RC_OK) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_free_struct; + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_free_struct; } - if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && - (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present - == ConfirmedServiceRequest_PR_defineNamedVariableList)) - { - request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList); - } - else { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_free_struct; - } + if ((mmsPdu->present == MmsPdu_PR_confirmedRequestPdu) && + (mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present + == ConfirmedServiceRequest_PR_defineNamedVariableList)) + { + request = &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.defineNamedVariableList); + } + else { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_free_struct; + } - MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDevice* device = MmsServer_getDevice(connection->server); - if (request->variableListName.present == ObjectName_PR_domainspecific) { + if (request->variableListName.present == ObjectName_PR_domainspecific) { - char domainName[65]; + char domainName[65]; - if (request->variableListName.choice.domainspecific.domainId.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_free_struct; - } + if (request->variableListName.choice.domainspecific.domainId.size > 64) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_free_struct; + } - StringUtils_createStringFromBufferInBuffer(domainName, - request->variableListName.choice.domainspecific.domainId.buf, - request->variableListName.choice.domainspecific.domainId.size); + StringUtils_createStringFromBufferInBuffer(domainName, + request->variableListName.choice.domainspecific.domainId.buf, + request->variableListName.choice.domainspecific.domainId.size); - MmsDomain* domain = MmsDevice_getDomain(device, domainName); + MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain == NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_free_struct; - } + if (domain == NULL) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_free_struct; + } #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) { + if (LinkedList_size(domain->namedVariableLists) < connection->server->maxDomainSpecificDataSets) { #else - if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) { + if (LinkedList_size(domain->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_DOMAIN_SPECIFIC_DATA_SETS) { #endif - char variableListName[65]; - - if (request->variableListName.choice.domainspecific.itemId.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_free_struct; - } - - StringUtils_createStringFromBufferInBuffer(variableListName, - request->variableListName.choice.domainspecific.itemId.buf, - request->variableListName.choice.domainspecific.itemId.size); - - if (MmsDomain_getNamedVariableList(domain, variableListName) != NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); - } - else { - MmsError mmsError; - - MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device, - request, variableListName, &mmsError); - - if (namedVariableList != NULL) { - - mmsError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection); - - if (mmsError == MMS_ERROR_NONE) { - MmsDomain_addNamedVariableList(domain, namedVariableList); - createDefineNamedVariableListResponse(invokeId, response); - } - else { - MmsNamedVariableList_destroy(namedVariableList); - mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); - } - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); - } + char variableListName[65]; + + if (request->variableListName.choice.domainspecific.itemId.size > 64) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_free_struct; + } + + StringUtils_createStringFromBufferInBuffer(variableListName, + request->variableListName.choice.domainspecific.itemId.buf, + request->variableListName.choice.domainspecific.itemId.size); + + if (MmsDomain_getNamedVariableList(domain, variableListName) != NULL) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); + } + else { + MmsError mmsError; + + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, domain, device, + request, variableListName, &mmsError); + + if (namedVariableList != NULL) { + + mmsError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_DOMAIN_SPECIFIC, domain, variableListName, connection); + + if (mmsError == MMS_ERROR_NONE) { + MmsDomain_addNamedVariableList(domain, namedVariableList); + createDefineNamedVariableListResponse(invokeId, response); + } + else { + MmsNamedVariableList_destroy(namedVariableList); + mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); + } } else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); } else if (request->variableListName.present == ObjectName_PR_aaspecific) { #if (CONFIG_MMS_SERVER_CONFIG_SERVICES_AT_RUNTIME == 1) - if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) { + if (LinkedList_size(connection->namedVariableLists) < connection->server->maxAssociationSpecificDataSets) { #else - if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) { + if (LinkedList_size(connection->namedVariableLists) < CONFIG_MMS_MAX_NUMBER_OF_ASSOCIATION_SPECIFIC_DATA_SETS) { #endif - char variableListName[65]; - - if (request->variableListName.choice.aaspecific.size > 64) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - goto exit_free_struct; - } - - StringUtils_createStringFromBufferInBuffer(variableListName, - request->variableListName.choice.aaspecific.buf, - request->variableListName.choice.aaspecific.size); - - if (MmsServerConnection_getNamedVariableList(connection, variableListName) != NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); - } - else { - MmsError mmsError; - - MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, - request, variableListName, &mmsError); - - if (namedVariableList != NULL) { - - if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) { - MmsServerConnection_addNamedVariableList(connection, namedVariableList); - createDefineNamedVariableListResponse(invokeId, response); - } - else { - MmsNamedVariableList_destroy(namedVariableList); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); - } - - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); - } - } - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); + char variableListName[65]; + + if (request->variableListName.choice.aaspecific.size > 64) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + goto exit_free_struct; + } + + StringUtils_createStringFromBufferInBuffer(variableListName, + request->variableListName.choice.aaspecific.buf, + request->variableListName.choice.aaspecific.size); + + if (MmsServerConnection_getNamedVariableList(connection, variableListName) != NULL) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); + } + else { + MmsError mmsError; + + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, + request, variableListName, &mmsError); + + if (namedVariableList != NULL) { + + if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_ASSOCIATION_SPECIFIC, NULL, variableListName, connection) == MMS_ERROR_NONE) { + MmsServerConnection_addNamedVariableList(connection, namedVariableList); + createDefineNamedVariableListResponse(invokeId, response); + } + else { + MmsNamedVariableList_destroy(namedVariableList); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } + + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, mmsError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_CAPABILITY_UNAVAILABLE); } else if (request->variableListName.present == ObjectName_PR_vmdspecific) { - LinkedList vmdScopeNVLs = MmsDevice_getNamedVariableLists(connection->server->device); + LinkedList vmdScopeNVLs = MmsDevice_getNamedVariableLists(connection->server->device); - if (LinkedList_size(vmdScopeNVLs) < CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS) { + if (LinkedList_size(vmdScopeNVLs) < CONFIG_MMS_MAX_NUMBER_OF_VMD_SPECIFIC_DATA_SETS) { - char variableListName[65]; + char variableListName[65]; - if (request->variableListName.choice.vmdspecific.size > 64) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); - goto exit_free_struct; - } + if (request->variableListName.choice.vmdspecific.size > 64) { + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_REQUEST_INVALID_ARGUMENT, response); + goto exit_free_struct; + } - StringUtils_createStringFromBufferInBuffer(variableListName, - request->variableListName.choice.vmdspecific.buf, - request->variableListName.choice.vmdspecific.size); + StringUtils_createStringFromBufferInBuffer(variableListName, + request->variableListName.choice.vmdspecific.buf, + request->variableListName.choice.vmdspecific.size); - if (mmsServer_getNamedVariableListWithName(MmsDevice_getNamedVariableLists(connection->server->device), variableListName) != NULL) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); - } - else { - MmsError mmsError; + if (mmsServer_getNamedVariableListWithName(MmsDevice_getNamedVariableLists(connection->server->device), variableListName) != NULL) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_OBJECT_EXISTS); + } + else { + MmsError mmsError; - MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, - request, variableListName, &mmsError); + MmsNamedVariableList namedVariableList = createNamedVariableList(connection->server, NULL, device, + request, variableListName, &mmsError); - if (namedVariableList != NULL) { - if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_VMD_SPECIFIC, NULL, variableListName, connection) - == MMS_ERROR_NONE) { - LinkedList_add(vmdScopeNVLs, (void*) namedVariableList); + if (namedVariableList != NULL) { + if (mmsServer_callVariableListChangedHandler(MMS_VARLIST_CREATE, MMS_VMD_SPECIFIC, NULL, variableListName, connection) + == MMS_ERROR_NONE) { + LinkedList_add(vmdScopeNVLs, (void*) namedVariableList); - createDefineNamedVariableListResponse(invokeId, response); - } - else { - MmsNamedVariableList_destroy(namedVariableList); - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); - } + createDefineNamedVariableListResponse(invokeId, response); + } + else { + MmsNamedVariableList_destroy(namedVariableList); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } - } - } - } + } + } + } } else mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_DEFINITION_TYPE_UNSUPPORTED); @@ -640,7 +640,7 @@ mmsServer_handleDefineNamedVariableListRequest( exit_free_struct: asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); - return; + return; } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ @@ -670,7 +670,7 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, varListResponse->listOfVariable.list.size = variableCount; varListResponse->listOfVariable.list.array = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member**) - GLOBAL_CALLOC(variableCount, sizeof(void*)); + GLOBAL_CALLOC(variableCount, sizeof(void*)); LinkedList variable = LinkedList_getNext(variables); @@ -679,7 +679,7 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, MmsNamedVariableListEntry variableEntry = (MmsNamedVariableListEntry) variable->data; varListResponse->listOfVariable.list.array[i] = (struct GetNamedVariableListAttributesResponse__listOfVariable__Member*) - GLOBAL_CALLOC(1, sizeof(struct GetNamedVariableListAttributesResponse__listOfVariable__Member)); + GLOBAL_CALLOC(1, sizeof(struct GetNamedVariableListAttributesResponse__listOfVariable__Member)); varListResponse->listOfVariable.list.array[i]->variableSpecification.present = VariableSpecification_PR_name; @@ -702,12 +702,12 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, domainspecific.itemId.size = strlen(variableEntry->variableName); if (variableEntry->arrayIndex != -1) { - varListResponse->listOfVariable.list.array[i]->alternateAccess = - mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName); + varListResponse->listOfVariable.list.array[i]->alternateAccess = + mmsClient_createAlternateAccessIndexComponent(variableEntry->arrayIndex, variableEntry->componentName); } else if (variableEntry->componentName) { - varListResponse->listOfVariable.list.array[i]->alternateAccess = - mmsClient_createAlternateAccessComponent(variableEntry->componentName); + varListResponse->listOfVariable.list.array[i]->alternateAccess = + mmsClient_createAlternateAccessComponent(variableEntry->componentName); } variable = LinkedList_getNext(variable); @@ -718,9 +718,9 @@ createGetNamedVariableListAttributesResponse(int invokeId, ByteBuffer* response, asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); if (res.encoded == -1) - return false; + return false; else - return true; + return true; } void @@ -736,44 +736,53 @@ mmsServer_handleGetNamedVariableListAttributesRequest( (void**) &request, buffer + bufPos, maxBufPos - bufPos); if (rval.code != RC_OK) { - mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); - goto exit_function; + mmsMsg_createMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); + goto exit_function; } if (request->present == ObjectName_PR_domainspecific) { - char domainName[65]; - char itemName[65]; + char domainName[65]; + char itemName[65]; - if ((request->choice.domainspecific.domainId.size > 64) || - (request->choice.domainspecific.itemId.size > 64)) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); - goto exit_function; - } + if ((request->choice.domainspecific.domainId.size > 64) || + (request->choice.domainspecific.itemId.size > 64)) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); + goto exit_function; + } - StringUtils_createStringFromBufferInBuffer(domainName, request->choice.domainspecific.domainId.buf, - request->choice.domainspecific.domainId.size); + StringUtils_createStringFromBufferInBuffer(domainName, request->choice.domainspecific.domainId.buf, + request->choice.domainspecific.domainId.size); - StringUtils_createStringFromBufferInBuffer(itemName, request->choice.domainspecific.itemId.buf, - request->choice.domainspecific.itemId.size); + StringUtils_createStringFromBufferInBuffer(itemName, request->choice.domainspecific.itemId.buf, + request->choice.domainspecific.itemId.size); MmsDevice* mmsDevice = MmsServer_getDevice(connection->server); MmsDomain* domain = MmsDevice_getDomain(mmsDevice, domainName); if (domain != NULL) { - MmsNamedVariableList variableList = - MmsDomain_getNamedVariableList(domain, itemName); + MmsNamedVariableList varList = MmsDomain_getNamedVariableList(domain, itemName); - if (variableList != NULL) { + if (varList) { - if (createGetNamedVariableListAttributesResponse(invokeId, response, variableList) == false) { + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_DOMAIN_SPECIFIC, domain, varList->name, connection); - /* encoding failed - probably because buffer size is too small for message */ - ByteBuffer_setSize(response, 0); + if (accessError == MMS_ERROR_NONE) { + if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + /* encoding failed - probably because buffer size is too small for message */ + ByteBuffer_setSize(response, 0); + + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + } + else { + if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); } + } else mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); @@ -785,43 +794,77 @@ mmsServer_handleGetNamedVariableListAttributesRequest( #if (MMS_DYNAMIC_DATA_SETS == 1) else if (request->present == ObjectName_PR_aaspecific) { - char listName[65]; + char listName[65]; - if (request->choice.aaspecific.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); - goto exit_function; - } + if (request->choice.aaspecific.size > 64) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); + goto exit_function; + } + + StringUtils_createStringFromBufferInBuffer(listName, request->choice.aaspecific.buf, + request->choice.aaspecific.size); + + MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName); + + if (varList) { + + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_ASSOCIATION_SPECIFIC, NULL, varList->name, connection); - StringUtils_createStringFromBufferInBuffer(listName, request->choice.aaspecific.buf, - request->choice.aaspecific.size); + if (accessError == MMS_ERROR_NONE) { + if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) { - MmsNamedVariableList varList = MmsServerConnection_getNamedVariableList(connection, listName); + /* encoding failed - probably because buffer size is too small for message */ + ByteBuffer_setSize(response, 0); - if (varList != NULL) - createGetNamedVariableListAttributesResponse(invokeId, response, varList); - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + } + else { + if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ else if (request->present == ObjectName_PR_vmdspecific) { - char listName[65]; + char listName[65]; + + if (request->choice.vmdspecific.size > 64) { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); + goto exit_function; + } + + StringUtils_createStringFromBufferInBuffer(listName, request->choice.vmdspecific.buf, + request->choice.vmdspecific.size); + + MmsDevice* mmsDevice = MmsServer_getDevice(connection->server); - if (request->choice.vmdspecific.size > 64) { - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OTHER); - goto exit_function; - } + MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName); - StringUtils_createStringFromBufferInBuffer(listName, request->choice.vmdspecific.buf, - request->choice.vmdspecific.size); + if (varList) { - MmsDevice* mmsDevice = MmsServer_getDevice(connection->server); + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_GET_DIRECTORY, MMS_VMD_SPECIFIC, NULL, varList->name, connection); - MmsNamedVariableList varList = mmsServer_getNamedVariableListWithName(mmsDevice->namedVariableLists, listName); + if (accessError == MMS_ERROR_NONE) { + if (createGetNamedVariableListAttributesResponse(invokeId, response, varList) == false) { - if (varList != NULL) - createGetNamedVariableListAttributesResponse(invokeId, response, varList); - else - mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + /* encoding failed - probably because buffer size is too small for message */ + ByteBuffer_setSize(response, 0); + + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER); + } + } + else { + if (DEBUG_MMS_SERVER) printf("MMS get named variable list attributes: variable list %s access error: %i\n", varList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } + else + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); } else { mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c index 11306e4d..6a9d909f 100644 --- a/src/mms/iso_mms/server/mms_write_service.c +++ b/src/mms/iso_mms/server/mms_write_service.c @@ -1,7 +1,7 @@ /* * mms_write_service.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -430,8 +430,19 @@ handleWriteNamedVariableListRequest( else { MmsNamedVariableList namedList = MmsDomain_getNamedVariableList(domain, nameIdStr); - if (namedList != NULL) { - createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + if (namedList) { + + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_DOMAIN_SPECIFIC, domain, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) { + createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + } + else { + if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s access error: %i\n", nameIdStr, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } else { if (DEBUG_MMS_SERVER) printf("MMS write: named variable list %s not found!\n", nameIdStr); @@ -448,8 +459,19 @@ handleWriteNamedVariableListRequest( MmsNamedVariableList namedList = mmsServer_getNamedVariableListWithName(connection->server->device->namedVariableLists, listName); - if (namedList != NULL) { - createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + if (namedList) { + + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_VMD_SPECIFIC, NULL, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) { + createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + } + else { + if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s access error: %i\n", namedList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } else { if (DEBUG_MMS_SERVER) printf("MMS write: vmd specific named variable list %s not found!\n", listName); @@ -465,8 +487,19 @@ handleWriteNamedVariableListRequest( MmsNamedVariableList namedList = MmsServerConnection_getNamedVariableList(connection, listName); - if (namedList != NULL) { - createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + if (namedList) { + + MmsError accessError = mmsServer_callVariableListChangedHandler(MMS_VARLIST_WRITE, MMS_ASSOCIATION_SPECIFIC, NULL, namedList->name, connection); + + if (accessError == MMS_ERROR_NONE) { + createWriteNamedVariableListResponse(connection, writeRequest, invokeId, namedList, response); + } + else { + if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s access error: %i\n", namedList->name, accessError); + + mmsMsg_createServiceErrorPdu(invokeId, response, accessError); + } + } else { if (DEBUG_MMS_SERVER) printf("MMS write: association specific named variable list %s not found!\n", listName); From 76dbcb4496d46ceb5b981788c4520dedb7438b25 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 4 Mar 2023 08:43:14 +0000 Subject: [PATCH 033/214] - IED server: Implemented read/write access control to RCBs (LIB61850-391) --- .../server_example_access_control.c | 13 +++++++ src/iec61850/inc/iec61850_server.h | 24 +++++++++++++ .../inc_private/mms_mapping_internal.h | 3 ++ src/iec61850/inc_private/reporting.h | 2 +- src/iec61850/server/impl/ied_server.c | 7 ++++ src/iec61850/server/mms_mapping/reporting.c | 35 ++++++++++++++++--- 6 files changed, 78 insertions(+), 6 deletions(-) diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index 024bf77a..dcb93d0f 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -75,6 +75,17 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, 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 + */ +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"); + + return false; +} + static void rcbEventHandler(void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType event, const char* parameterName, MmsDataAccessError serviceError) { @@ -167,6 +178,8 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL); + IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); /* By default access to variables with FC=DC and FC=CF is not allowed. diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 9d34084c..93bb02e2 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1622,6 +1622,30 @@ typedef void (*IedServer_RCBEventHandler) (void* parameter, ReportControlBlock* LIB61850_API void IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler 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); + /**@}*/ /** diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index c22311f6..c42df272 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -335,6 +335,9 @@ struct sMmsMapping { IedServer_RCBEventHandler rcbEventHandler; void* rcbEventHandlerParameter; + IedServer_RCBAccessHandler rcbAccessHandler; + void* rcbAccessHandlerParameter; + IedServer_DataSetAccessHandler dataSetAccessHandler; void* dataSetAccessHandlerParameter; }; diff --git a/src/iec61850/inc_private/reporting.h b/src/iec61850/inc_private/reporting.h index a4e3433f..558343c2 100644 --- a/src/iec61850/inc_private/reporting.h +++ b/src/iec61850/inc_private/reporting.h @@ -140,7 +140,7 @@ LIB61850_INTERNAL MmsDataAccessError Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* elementName, MmsValue* value, MmsServerConnection connection); -LIB61850_INTERNAL void +LIB61850_INTERNAL bool ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName); LIB61850_INTERNAL void diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index d748bf5a..c398e143 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -689,6 +689,13 @@ IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler handler, self->mmsMapping->rcbEventHandlerParameter = parameter; } +void +IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter) +{ + self->mmsMapping->rcbAccessHandler = handler; + self->mmsMapping->rcbAccessHandlerParameter = parameter; +} + void IedServer_destroy(IedServer self) { diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 3b170a54..d0779138 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1697,21 +1697,35 @@ checkReservationTimeout(MmsMapping* self, ReportControl* rc) } } -void +bool ReportControl_readAccess(ReportControl* rc, MmsMapping* mmsMapping, MmsServerConnection connection, char* elementName) { - (void)elementName; + bool accessAllowed = true; + MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS; /* check reservation timeout */ if (rc->buffered) { checkReservationTimeout(mmsMapping, rc); } - if (mmsMapping->rcbEventHandler) { - ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(mmsMapping->iedServer, connection); + ClientConnection clientConnection = NULL; + + 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; + } + } - mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, DATA_ACCESS_ERROR_SUCCESS); + if (mmsMapping->rcbEventHandler) { + mmsMapping->rcbEventHandler(mmsMapping->rcbEventHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_GET_PARAMETER, elementName, accessError); } + + return accessAllowed; } static bool @@ -1809,6 +1823,15 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + /* check if write access to RCB is allowed on this connection */ + if (self->rcbAccessHandler) { + if (self->rcbAccessHandler(self->rcbAccessHandlerParameter, rc->rcb, clientConnection, RCB_EVENT_SET_PARAMETER) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + goto exit_function_only_tracking; + } + } + /* check reservation timeout for buffered RCBs */ if (rc->buffered) { @@ -2491,6 +2514,8 @@ exit_function: ReportControl_unlockNotify(rc); +exit_function_only_tracking: + #if (CONFIG_IEC61850_SERVICE_TRACKING == 1) if (rc->buffered) updateGenericTrackingObjectValues(self, rc, IEC61850_SERVICE_TYPE_SET_BRCB_VALUES, retVal); From dc5bd43f0fe87269275c81058df076c2f59e84a5 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 5 Mar 2023 08:25:02 +0000 Subject: [PATCH 034/214] - IED server: fixed read handling in RCB access control (LIB61850-391) --- .../server_example_access_control.c | 7 +++- src/iec61850/server/mms_mapping/mms_mapping.c | 35 +++++++++++-------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index dcb93d0f..2d8bde93 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -83,7 +83,12 @@ rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conn { printf("RCB: %s access: %s\n", ReportControlBlock_getName(rcb), operation == RCB_EVENT_GET_PARAMETER ? "READ" : "WRITE"); - return false; + if (operation == RCB_EVENT_GET_PARAMETER) { + return true; + } + else { + return false; + } } static void diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index a33e31b2..a91ace26 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -25,6 +25,7 @@ #include "mms_mapping.h" #include "mms_mapping_internal.h" #include "mms_server_internal.h" +#include "mms_value_internal.h" #include "stack_config.h" #include "mms_goose.h" @@ -68,6 +69,8 @@ typedef struct uint64_t reservationTimeout; } SettingGroup; +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + #if (CONFIG_IEC61850_CONTROL_SERVICE == 1) bool @@ -3155,31 +3158,35 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo continue; if (strlen(rc->name) == variableIdLen) { - if (strncmp(variableId, rc->name, variableIdLen) == 0) { - + if (strncmp(variableId, rc->name, variableIdLen) == 0) + { char* elementName = MmsMapping_getNextNameElement(reportName); - ReportControl_readAccess(rc, self, connection, elementName); - MmsValue* value = NULL; + if (ReportControl_readAccess(rc, self, connection, elementName)) + { #if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore_wait(rc->rcbValuesLock); + Semaphore_wait(rc->rcbValuesLock); #endif - if (elementName != NULL) - value = ReportControl_getRCBValue(rc, elementName); - else - value = rc->rcbValues; + if (elementName != NULL) + value = ReportControl_getRCBValue(rc, elementName); + else + value = rc->rcbValues; - if (value) { - value = MmsValue_clone(value); - MmsValue_setDeletableRecursive(value); - } + if (value) { + value = MmsValue_clone(value); + MmsValue_setDeletableRecursive(value); + } #if (CONFIG_MMS_THREADLESS_STACK != 1) - Semaphore_post(rc->rcbValuesLock); + Semaphore_post(rc->rcbValuesLock); #endif + } + else { + value = &objectAccessDenied; + } retValue = value; From 41ed0dd51ae2f7a79ceba4b1b441fa4cf56e9e0e Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 10 Mar 2023 17:14:19 +0000 Subject: [PATCH 035/214] - implemented access control callbacks for logs and LCBs (LIB61850-392) --- CMakeLists.txt | 5 ++ .../server_example_access_control.c | 17 ++++- .../server_example_logging.c | 26 ++++++++ src/iec61850/inc/iec61850_dynamic_model.h | 6 ++ src/iec61850/inc/iec61850_server.h | 63 ++++++++++++------- src/iec61850/inc_private/logging.h | 2 +- .../inc_private/mms_mapping_internal.h | 6 ++ src/iec61850/server/impl/ied_server.c | 14 +++++ src/iec61850/server/mms_mapping/logging.c | 49 ++++++++++++--- src/iec61850/server/mms_mapping/mms_mapping.c | 35 +++++++++-- src/iec61850/server/model/dynamic_model.c | 12 ++++ src/mms/inc/mms_server.h | 20 ++++++ src/mms/inc_private/mms_server_internal.h | 3 + src/mms/iso_mms/server/mms_journal_service.c | 14 +++++ src/mms/iso_mms/server/mms_server.c | 7 +++ 15 files changed, 239 insertions(+), 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28477596..80cb5d17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,11 @@ set(USE_PREBUILD_MBEDTLS 1) set(MBEDTLS_INCLUDE_DIR ${CONFIG_EXTERNAL_MBEDTLS_INCLUDE_PATH}) endif(CONFIG_USE_EXTERNAL_MBEDTLS_DYNLIB) +if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h) +set(FOUND_SQLITE3_SOURCE 1) +message("Found sqlite3 source in third_party folder -> can compile with log service support") +endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/sqlite/sqlite3.h) + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28) set(WITH_MBEDTLS 1) set(MBEDTLS_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/third_party/mbedtls/mbedtls-2.28/include") diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index 2d8bde93..7c18fd46 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -78,7 +78,7 @@ connectionHandler (IedServer self, ClientConnection connection, bool connected, /* * This handler is called before the rcbEventHandler and can be use to allow or permit read or write access to the RCB */ -bool +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"); @@ -91,6 +91,19 @@ rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conn } } +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) { @@ -185,6 +198,8 @@ main(int argc, char** argv) IedServer_setRCBAccessHandler(iedServer, rcbAccessHandler, NULL); + IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); + IedServer_setRCBEventHandler(iedServer, rcbEventHandler, NULL); /* By default access to variables with FC=DC and FC=CF is not allowed. diff --git a/examples/server_example_logging/server_example_logging.c b/examples/server_example_logging/server_example_logging.c index 51c7a0df..ee388be8 100644 --- a/examples/server_example_logging/server_example_logging.c +++ b/examples/server_example_logging/server_example_logging.c @@ -115,6 +115,28 @@ entryDataCallback (void* parameter, const char* dataRef, const uint8_t* data, in return true; } +static bool +lcbAccessHandler(void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation) +{ + printf("%s access to LCB %s from %s\n", operation == LCB_EVENT_GET_PARAMETER ? "read" : "write", LogControlBlock_getName(lcb), ClientConnection_getPeerAddress(connection)); + + /* only allow read access */ + if (operation == LCB_EVENT_GET_PARAMETER) { + return true; + } + else { + return false; + } +} + +static bool +logAccessHandler(void* parameter, const char* logName, ClientConnection connection) +{ + printf("Access to log %s from %s\n", logName, ClientConnection_getPeerAddress(connection)); + + return false; +} + int main(int argc, char** argv) { @@ -147,6 +169,10 @@ main(int argc, char** argv) IedServer_setConnectionIndicationHandler(iedServer, (IedConnectionIndicationHandler) connectionHandler, NULL); + IedServer_setLCBAccessHandler(iedServer, lcbAccessHandler, NULL); + + IedServer_setLogAccessHandler(iedServer, logAccessHandler, NULL); + LogStorage statusLog = SqliteLogStorage_createInstance("log_status.db"); LogStorage_setMaxLogEntries(statusLog, 10); diff --git a/src/iec61850/inc/iec61850_dynamic_model.h b/src/iec61850/inc/iec61850_dynamic_model.h index 32f6ff04..693dc7f4 100644 --- a/src/iec61850/inc/iec61850_dynamic_model.h +++ b/src/iec61850/inc/iec61850_dynamic_model.h @@ -395,6 +395,12 @@ LIB61850_API LogControlBlock* LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSetName, const char* logRef, uint8_t trgOps, uint32_t intgPd, bool logEna, bool reasonCode); +LIB61850_API const char* +LogControlBlock_getName(LogControlBlock* self); + +LIB61850_API LogicalNode* +LogControlBlock_getParent(LogControlBlock* self); + /** * \brief create a log (used by the IEC 61850 log service) * diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 93bb02e2..ec023b4d 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1622,30 +1622,6 @@ typedef void (*IedServer_RCBEventHandler) (void* parameter, ReportControlBlock* LIB61850_API void IedServer_setRCBEventHandler(IedServer self, IedServer_RCBEventHandler 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); - /**@}*/ /** @@ -1865,6 +1841,45 @@ typedef MmsDataAccessError LIB61850_API void IedServer_setReadAccessHandler(IedServer self, ReadAccessHandler handler, void* parameter); +/** + * \brief Callback that is called in case of RCB access to give the user the opportunity to block or allow the operation + * + * \note This callback is called before the IedServer_RCBEventHandler and only in case of operations (RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER, RCB_EVENT_ENABLE + * + * \param parameter user provided parameter + * \param rcb affected report control block + * \param connection client connection that is involved + * \param operation one of the following operation event types: RCB_EVENT_GET_PARAMETER, RCB_EVENT_SET_PARAMETER + */ +typedef bool +(*IedServer_RCBAccessHandler) (void* parameter, ReportControlBlock* rcb, ClientConnection connection, IedServer_RCBEventType operation); + +/** + * \brief Set a handler to control read and write access to report control blocks (RCBs) + * + * \param self the instance of IedServer to operate on. + * \param handler the event handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler, void* parameter); + +typedef enum { + LCB_EVENT_GET_PARAMETER, + LCB_EVENT_SET_PARAMETER +} IedServer_LCBEventType; + +typedef bool +(*IedServer_LCBAccessHandler) (void* parameter, LogControlBlock* lcb, ClientConnection connection, IedServer_LCBEventType operation); + +LIB61850_API void +IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter); + +typedef bool +(*IedServer_LogAccessHandler) (void* parameter, const char* logName, ClientConnection connection); + +LIB61850_API void +IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter); typedef enum { DATASET_CREATE, diff --git a/src/iec61850/inc_private/logging.h b/src/iec61850/inc_private/logging.h index 7684739a..82ef712b 100644 --- a/src/iec61850/inc_private/logging.h +++ b/src/iec61850/inc_private/logging.h @@ -121,7 +121,7 @@ LIB61850_INTERNAL void Logging_processIntegrityLogs(MmsMapping* self, uint64_t currentTimeInMs); LIB61850_INTERNAL MmsValue* -LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig); +LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection); LIB61850_INTERNAL MmsDataAccessError LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index c42df272..09aba970 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -338,6 +338,12 @@ struct sMmsMapping { IedServer_RCBAccessHandler rcbAccessHandler; void* rcbAccessHandlerParameter; + IedServer_LCBAccessHandler lcbAccessHandler; + void* lcbAccessHandlerParameter; + + IedServer_LogAccessHandler logAccessHandler; + void* logAccessHandlerParameter; + IedServer_DataSetAccessHandler dataSetAccessHandler; void* dataSetAccessHandlerParameter; }; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index c398e143..fb39ead3 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -696,6 +696,20 @@ IedServer_setRCBAccessHandler(IedServer self, IedServer_RCBAccessHandler handler self->mmsMapping->rcbAccessHandlerParameter = parameter; } +void +IedServer_setLCBAccessHandler(IedServer self, IedServer_LCBAccessHandler handler, void* parameter) +{ + self->mmsMapping->lcbAccessHandler = handler; + self->mmsMapping->lcbAccessHandlerParameter = parameter; +} + +void +IedServer_setLogAccessHandler(IedServer self, IedServer_LogAccessHandler handler, void* parameter) +{ + self->mmsMapping->logAccessHandler = handler; + self->mmsMapping->logAccessHandlerParameter = parameter; +} + void IedServer_destroy(IedServer self) { diff --git a/src/iec61850/server/mms_mapping/logging.c b/src/iec61850/server/mms_mapping/logging.c index e841feb2..c55a1ff4 100644 --- a/src/iec61850/server/mms_mapping/logging.c +++ b/src/iec61850/server/mms_mapping/logging.c @@ -1,7 +1,7 @@ /* * logging.c * - * Copyright 2016-2022 Michael Zillgith + * Copyright 2016-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -43,6 +43,8 @@ #if (CONFIG_IEC61850_LOG_SERVICE == 1) +static MmsValue objectAccessDenied = {MMS_DATA_ACCESS_ERROR, false, {DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED}}; + LogInstance* LogInstance_create(LogicalNode* parentLN, const char* name) { @@ -520,6 +522,19 @@ LIBIEC61850_LOG_SVC_writeAccessLogControlBlock(MmsMapping* self, MmsDomain* doma if (logControl == NULL) { return DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; } + else + { + if (self->lcbAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_SET_PARAMETER) == false) { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + + goto exit_function; + } + } + } if (strcmp(varName, "LogEna") == 0) { bool logEna = MmsValue_getBoolean(value); @@ -699,7 +714,7 @@ exit_function: } MmsValue* -LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig) +LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* variableIdOrig, MmsServerConnection connection) { MmsValue* value = NULL; @@ -723,27 +738,41 @@ LIBIEC61850_LOG_SVC_readAccessControlBlock(MmsMapping* self, MmsDomain* domain, char* varName = MmsMapping_getNextNameElement(objectName); - if (varName != NULL) + if (varName) *(varName - 1) = 0; LogControl* logControl = lookupLogControl(self, domain, lnName, objectName); - if (logControl != NULL) { + if (logControl) + { + bool allowAccess = true; + + if (self->lcbAccessHandler) + { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); - updateLogStatusInLCB(logControl); + if (self->lcbAccessHandler(self->lcbAccessHandlerParameter, logControl->logControlBlock, clientConnection, LCB_EVENT_GET_PARAMETER) == false) { + allowAccess = false; - if (varName != NULL) { - value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + value = &objectAccessDenied; + } } - else { - value = logControl->mmsValue; + + if (allowAccess) { + updateLogStatusInLCB(logControl); + + if (varName) { + value = MmsValue_getSubElement(logControl->mmsValue, logControl->mmsType, varName); + } + else { + value = logControl->mmsValue; + } } } return value; } - static char* createDataSetReferenceForDefaultDataSet(LogControlBlock* lcb, LogControl* logControl) { diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index a91ace26..396fcc96 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3115,7 +3115,7 @@ mmsReadHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerCo #if (CONFIG_IEC61850_LOG_SERVICE == 1) /* LOG control block - LG */ if (isLogControlBlock(separator)) { - retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId); + retValue = LIBIEC61850_LOG_SVC_readAccessControlBlock(self, domain, variableId, connection); goto exit_function; } #endif @@ -3410,9 +3410,7 @@ checkDataSetAccess(MmsMapping* self, MmsServerConnection connection, MmsVariable StringUtils_appendString(dataSetRef, 129, listName); } - accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, - private_IedServer_getClientConnectionByHandle(self->iedServer, connection), - operation, dataSetRef); + accessGranted = self->dataSetAccessHandler(self->dataSetAccessHandlerParameter, clientConnection, operation, dataSetRef); } return accessGranted; @@ -3596,6 +3594,31 @@ variableListAccessHandler (void* parameter, MmsVariableListAccessType accessType return allow; } +#if (CONFIG_IEC61850_LOG_SERVICE == 1) +static bool +mmsReadJournalHandler(void* parameter, MmsDomain* domain, const char* logName, MmsServerConnection connection) +{ + bool allowAccess = true; + + MmsMapping* self = (MmsMapping*)parameter; + + if (self->logAccessHandler) { + char logReference[130]; + logReference[0] = 0; + + StringUtils_appendString(logReference, 130, MmsDomain_getName(domain)); + StringUtils_appendString(logReference, 130, "/"); + StringUtils_appendString(logReference, 130, logName); + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->logAccessHandler(self->logAccessHandlerParameter, logReference, clientConnection); + } + + return allowAccess; +} +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ + void MmsMapping_installHandlers(MmsMapping* self) { @@ -3604,6 +3627,10 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); + +#if (CONFIG_IEC61850_LOG_SERVICE == 1) + MmsServer_installReadJournalHandler(self->mmsServer, mmsReadJournalHandler, (void*) self); +#endif /* (CONFIG_IEC61850_LOG_SERVICE == 1) */ } void diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 18933c46..9b0a53fd 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -347,6 +347,18 @@ LogControlBlock_create(const char* name, LogicalNode* parent, const char* dataSe return self; } +const char* +LogControlBlock_getName(LogControlBlock* self) +{ + return self->name; +} + +LogicalNode* +LogControlBlock_getParent(LogControlBlock* self) +{ + return self->parent; +} + static void LogicalNode_addReportControlBlock(LogicalNode* self, ReportControlBlock* rcb) { diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index b9c22417..ef4831d1 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -90,6 +90,26 @@ typedef MmsError (*MmsNamedVariableListAccessHandler)(void* parameter, MmsVariab LIB61850_INTERNAL void 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); + /** * \brief ObtainFile service callback handler * diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index fc875597..087ff42c 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -126,6 +126,9 @@ struct sMmsServer { MmsNamedVariableListAccessHandler variableListAccessHandler; void* variableListAccessHandlerParameter; + MmsReadJournalHandler readJournalHandler; + void* readJournalHandlerParameter; + AcseAuthenticator authenticator; void* authenticatorParameter; diff --git a/src/mms/iso_mms/server/mms_journal_service.c b/src/mms/iso_mms/server/mms_journal_service.c index 22980f76..0aadde40 100644 --- a/src/mms/iso_mms/server/mms_journal_service.c +++ b/src/mms/iso_mms/server/mms_journal_service.c @@ -462,6 +462,20 @@ mmsServer_handleReadJournalRequest( if (DEBUG_MMS_SERVER) printf("MMS_SERVER: readJournal - read journal %s ...\n", mmsJournal->name); + MmsServer mmsServer = connection->server; + + if (mmsServer->readJournalHandler) + { + if (mmsServer->readJournalHandler(mmsServer->readJournalHandlerParameter, mmsDomain, logName, connection) == false) + { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + + /* TODO log access error */ + + return; + } + } + struct sJournalEncoder encoder; encoder.buffer = response->buffer; diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index e6cccfa5..311266c1 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -365,6 +365,13 @@ MmsServer_installVariableListAccessHandler(MmsServer self, MmsNamedVariableListA self->variableListAccessHandlerParameter = parameter; } +void +MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handler, void* parameter) +{ + self->readJournalHandler = handler; + self->readJournalHandlerParameter = parameter; +} + void MmsServer_setClientAuthenticator(MmsServer self, AcseAuthenticator authenticator, void* authenticatorParameter) { From 7e3b7991d53fe9ade1599490335319b2d4ba5b2f Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 10 Mar 2023 18:34:10 +0000 Subject: [PATCH 036/214] - added doxygen comments for new access callback functions (LIB61850-381) --- src/iec61850/inc/iec61850_server.h | 50 +++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index ec023b4d..f45c68e1 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1869,15 +1869,46 @@ typedef enum { 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* logName, ClientConnection connection); +(*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); @@ -1889,9 +1920,26 @@ typedef enum { 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); From a4a58e42502dcde6d4d2e8e95cd62b965885e85b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 17 Mar 2023 07:48:54 +0000 Subject: [PATCH 037/214] - IED server: check user permissions on data set when enabling RCB or changing RCB data sets (LIB61850-393) --- .../server_example_access_control.c | 26 +++++- src/iec61850/server/mms_mapping/reporting.c | 81 +++++++++++++++++-- src/mms/iso_mms/server/mms_read_service.c | 8 +- src/mms/iso_mms/server/mms_server.c | 1 - src/mms/iso_mms/server/mms_server_common.c | 7 +- 5 files changed, 102 insertions(+), 21 deletions(-) diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index 7c18fd46..eec313eb 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -87,7 +87,8 @@ rcbAccessHandler(void* parameter, ReportControlBlock* rcb, ClientConnection conn return true; } else { - return false; + /* change to false to disallow write access to control block */ + return true; } } @@ -133,6 +134,18 @@ dataSetAccessHandler(void* parameter, ClientConnection connection, IedServer_Dat 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; +} + int main(int argc, char** argv) { @@ -196,10 +209,13 @@ main(int argc, char** argv) 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. @@ -208,8 +224,16 @@ main(int argc, char** argv) */ 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); + /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, tcpPort); diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index d0779138..a097a35c 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -1,7 +1,7 @@ /* * reporting.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -673,6 +673,43 @@ createDataSetValuesShadowBuffer(ReportControl* rc) } } +static bool +checkIfClientHasAccessToDataSetEntries(MmsMapping* mapping, MmsServerConnection connection, MmsNamedVariableList mmsVariableList) +{ + bool accessAllowed = true; + + if (connection) { + + LinkedList entryElem = LinkedList_getNext(mmsVariableList->listOfVariables); + + while (entryElem) { + MmsNamedVariableListEntry entry = (MmsNamedVariableListEntry)LinkedList_getData(entryElem); + + MmsValue* entryValue = mmsServer_getValue(mapping->mmsServer, entry->domain, entry->variableName, connection, true); + + if (entryValue) { + + if (MmsValue_getType(entryValue) == MMS_DATA_ACCESS_ERROR) { + accessAllowed = false; + } + + MmsValue_deleteConditional(entryValue); + } + else { + accessAllowed = false; + } + + if (accessAllowed == false) + break; + + entryElem = LinkedList_getNext(entryElem); + } + + } + + return accessAllowed; +} + static bool updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsServerConnection connection) { @@ -760,11 +797,32 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } } - if (dataSetValue && dataSetChanged) { + if (dataSetValue) { const char* dataSetName = MmsValue_toString(dataSetValue); DataSet* dataSet = IedModel_lookupDataSet(mapping->model, dataSetName); + if (dataSet) { + + char domainNameBuf[130]; + + MmsMapping_getMmsDomainFromObjectReference(dataSetName, domainNameBuf); + + MmsDomain* dsDomain = MmsDevice_getDomain(mapping->mmsDevice, domainNameBuf); + + if (dsDomain) { + MmsNamedVariableList namedVariableList = MmsDomain_getNamedVariableList(dsDomain, dataSet->name); + + if (namedVariableList) { + if (checkIfClientHasAccessToDataSetEntries(mapping, connection, namedVariableList) == false) { + goto exit_function; + } + } + + } + + } + #if (MMS_DYNAMIC_DATA_SETS == 1) if (dataSet == NULL) { dataSet = MmsMapping_getDomainSpecificDataSet(mapping, dataSetName); @@ -779,19 +837,28 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsNamedVariableList mmsVariableList = MmsServerConnection_getNamedVariableList(connection, dataSetName + 1); - if (mmsVariableList != NULL) + if (mmsVariableList) { + if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) { + goto exit_function; + } + dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); + } } } - } /* check for VMD specific data set */ else if (dataSetName[0] == '/') { MmsNamedVariableList mmsVariableList = MmsDevice_getNamedVariableListWithName(mapping->mmsDevice, dataSetName + 1); - if (mmsVariableList != NULL) + if (mmsVariableList) { + if (checkIfClientHasAccessToDataSetEntries(mapping, connection, mmsVariableList) == false) { + goto exit_function; + } + dataSet = MmsMapping_createDataSetByNamedVariableList(mapping, mmsVariableList); + } } } @@ -810,7 +877,7 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ - if (dataSetChanged == true) { + if (dataSetChanged) { /* delete pending event and create buffer for new data set */ deleteDataSetValuesShadowBuffer(rc); @@ -830,7 +897,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, GLOBAL_FREEMEM(rc->inclusionFlags); rc->inclusionFlags = (uint8_t*) GLOBAL_CALLOC(dataSet->elementCount, sizeof(uint8_t)); - } success = true; @@ -862,7 +928,6 @@ createDataSetReferenceForDefaultDataSet(ReportControlBlock* rcb, ReportControl* return dataSetReference; } - static MmsValue* createOptFlds(ReportControlBlock* reportControlBlock) { diff --git a/src/mms/iso_mms/server/mms_read_service.c b/src/mms/iso_mms/server/mms_read_service.c index 3193c699..2b33dc41 100644 --- a/src/mms/iso_mms/server/mms_read_service.c +++ b/src/mms/iso_mms/server/mms_read_service.c @@ -1,7 +1,7 @@ /* * mms_read_service.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -695,13 +695,9 @@ createNamedVariableListResponse(MmsServerConnection connection, MmsNamedVariable LinkedList /**/ values = LinkedList_create(); LinkedList variables = MmsNamedVariableList_getVariableList(namedList); - int variableCount = LinkedList_size(variables); - - int i; - LinkedList variable = LinkedList_getNext(variables); - for (i = 0; i < variableCount; i++) { + while (variable) { MmsNamedVariableListEntry variableListEntry = (MmsNamedVariableListEntry) variable->data; diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index 311266c1..9a203713 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -572,7 +572,6 @@ exit_function: return value; } - MmsDevice* MmsServer_getDevice(MmsServer self) { diff --git a/src/mms/iso_mms/server/mms_server_common.c b/src/mms/iso_mms/server/mms_server_common.c index 62f9e0d5..455a26ec 100644 --- a/src/mms/iso_mms/server/mms_server_common.c +++ b/src/mms/iso_mms/server/mms_server_common.c @@ -391,7 +391,7 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char LinkedList element = LinkedList_getNext(namedVariableLists); - while (element != NULL) { + while (element) { MmsNamedVariableList varList = (MmsNamedVariableList) element->data; if (strcmp(MmsNamedVariableList_getName(varList), variableListName) == 0) { @@ -405,14 +405,13 @@ mmsServer_getNamedVariableListWithName(LinkedList namedVariableLists, const char return variableList; } - void mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListName) { LinkedList previousElement = namedVariableLists; LinkedList element = LinkedList_getNext(namedVariableLists); - while (element != NULL ) { + while (element) { MmsNamedVariableList varList = (MmsNamedVariableList) element->data; if (strcmp(MmsNamedVariableList_getName(varList), variableListName) @@ -428,5 +427,3 @@ mmsServer_deleteVariableList(LinkedList namedVariableLists, char* variableListNa element = LinkedList_getNext(element); } } - - From aa863f35f0df1f28f8904077ab28ecb6de4059af Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 17 Mar 2023 18:58:54 +0000 Subject: [PATCH 038/214] - IED server: added callback to control access to data model directories (get-name-list requests)(LIB61850-396) --- .../server_example_access_control.c | 24 +++ src/iec61850/inc/iec61850_server.h | 13 ++ .../inc_private/mms_mapping_internal.h | 3 + src/iec61850/server/impl/ied_server.c | 7 + src/iec61850/server/mms_mapping/mms_mapping.c | 41 +++++ src/mms/inc/mms_server.h | 12 ++ src/mms/inc_private/mms_server_internal.h | 3 + .../iso_mms/server/mms_get_namelist_service.c | 151 +++++++++++++----- src/mms/iso_mms/server/mms_server.c | 7 + 9 files changed, 220 insertions(+), 41 deletions(-) diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index eec313eb..e0b789af 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -146,6 +146,28 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu 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) { @@ -234,6 +256,8 @@ main(int argc, char** argv) */ 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); diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index f45c68e1..24f4b776 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -1943,6 +1943,19 @@ typedef bool LIB61850_API void IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler handler, void* parameter); +typedef enum { + DIRECTORY_CAT_LD_LIST, + DIRECTORY_CAT_DATA_LIST, + DIRECTORY_CAT_DATASET_LIST, + DIRECTORY_CAT_LOG_LIST +} IedServer_DirectoryCategory; + +typedef bool +(*IedServer_DirectoryAccessHandler) (void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice); + +LIB61850_API void +IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 09aba970..a66e815a 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -346,6 +346,9 @@ struct sMmsMapping { IedServer_DataSetAccessHandler dataSetAccessHandler; void* dataSetAccessHandlerParameter; + + IedServer_DirectoryAccessHandler directoryAccessHandler; + void* directoryAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index fb39ead3..08c555d7 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1945,3 +1945,10 @@ IedServer_setDataSetAccessHandler(IedServer self, IedServer_DataSetAccessHandler self->mmsMapping->dataSetAccessHandler = handler; self->mmsMapping->dataSetAccessHandlerParameter = parameter; } + +void +IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter) +{ + self->mmsMapping->directoryAccessHandler = handler; + self->mmsMapping->directoryAccessHandlerParameter = parameter; +} diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 396fcc96..2af5f228 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3228,6 +3228,46 @@ unselectControlsForConnection(MmsMapping* self, MmsServerConnection connection) } #endif /* (CONFIG_IEC61850_CONTROL_SERVICE == 1) */ +static bool +mmsGetNameListHandler(void* parameter, MmsGetNameListType nameListType, MmsDomain* domain, MmsServerConnection connection) +{ + MmsMapping* self = (MmsMapping*) parameter; + + bool allowAccess = true; + + if (self->directoryAccessHandler) { + + LogicalDevice* ld = NULL; + + IedServer_DirectoryCategory category = DIRECTORY_CAT_DATA_LIST; + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + if (domain) { + ld = IedModel_getDevice(self->model, MmsDomain_getName(domain)); + + if (ld == NULL) { + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsGetNameListHandler -> LD not found!\n"); + } + } + + /* convert type to category */ + if (nameListType == MMS_GETNAMELIST_DATA) + category = DIRECTORY_CAT_DATA_LIST; + else if (nameListType == MMS_GETNAMELIST_DATASETS) + category = DIRECTORY_CAT_DATASET_LIST; + else if (nameListType == MMS_GETNAMELIST_DOMAINS) + category = DIRECTORY_CAT_LD_LIST; + else if (nameListType == MMS_GETNAMELIST_JOURNALS) + category = DIRECTORY_CAT_LOG_LIST; + + allowAccess = self->directoryAccessHandler(self->directoryAccessHandlerParameter, clientConnection, category, ld); + } + + return allowAccess; +} + static void /* is called by MMS server layer and runs in the connection handling thread */ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerEvent event) { @@ -3627,6 +3667,7 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (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); diff --git a/src/mms/inc/mms_server.h b/src/mms/inc/mms_server.h index ef4831d1..15248789 100644 --- a/src/mms/inc/mms_server.h +++ b/src/mms/inc/mms_server.h @@ -110,6 +110,18 @@ typedef bool (*MmsReadJournalHandler)(void* parameter, MmsDomain* domain, const 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 * diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index 087ff42c..ec70d359 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -129,6 +129,9 @@ struct sMmsServer { MmsReadJournalHandler readJournalHandler; void* readJournalHandlerParameter; + MmsGetNameListHandler getNameListHandler; + void* getNameListHandlerParameter; + AcseAuthenticator authenticator; void* authenticatorParameter; diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 005aa4c6..2170f8c1 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -1,7 +1,7 @@ /* * mms_get_namelist_service.c * - * Copyright 2013-2022 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -190,20 +190,29 @@ getJournalListDomainSpecific(MmsServerConnection connection, char* domainName) MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { - nameList = LinkedList_create(); + if (domain) { - if (domain->journals != NULL) { + bool allowAccess = true; - LinkedList journalList = domain->journals; + if (connection->server->getNameListHandler) { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_JOURNALS, domain, connection); + } - while ((journalList = LinkedList_getNext(journalList)) != NULL) { + if (allowAccess) { + nameList = LinkedList_create(); - MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); + if (domain->journals != NULL) { - LinkedList_add(nameList, (void*) journal->name); - } + LinkedList journalList = domain->journals; + + while ((journalList = LinkedList_getNext(journalList)) != NULL) { + + MmsJournal journal = (MmsJournal) LinkedList_getData(journalList); + LinkedList_add(nameList, (void*) journal->name); + } + + } } } @@ -219,46 +228,56 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { - nameList = LinkedList_create(); - MmsVariableSpecification** variables = domain->namedVariables; + if (domain) { - int i; + bool allowAccess = true; - LinkedList element = nameList; + if (connection->server->getNameListHandler) { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection); + } + + if (allowAccess) { + nameList = LinkedList_create(); + MmsVariableSpecification** variables = domain->namedVariables; + + int i; + + LinkedList element = nameList; #if (CONFIG_MMS_SORT_NAME_LIST == 1) - int* index = (int*) GLOBAL_MALLOC(sizeof(int) * domain->namedVariablesCount); + int* index = (int*) GLOBAL_MALLOC(sizeof(int) * domain->namedVariablesCount); - for (i = 0; i < domain->namedVariablesCount; i++) - index[i] = i; + for (i = 0; i < domain->namedVariablesCount; i++) + index[i] = i; - sortIndex(index, domain->namedVariablesCount, domain->namedVariables); + sortIndex(index, domain->namedVariablesCount, domain->namedVariables); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ - for (i = 0; i < domain->namedVariablesCount; i++) { + for (i = 0; i < domain->namedVariablesCount; i++) { #if (CONFIG_MMS_SORT_NAME_LIST == 1) - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); #else - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); #endif #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1) - char* prefix = variables[index[i]]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); + char* prefix = variables[index[i]]->name; + element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); #else - char* prefix = variables[i]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); + char* prefix = variables[i]->name; + element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ - } + } #if (CONFIG_MMS_SORT_NAME_LIST == 1) - GLOBAL_FREEMEM(index); + GLOBAL_FREEMEM(index); #endif + + } } return nameList; @@ -293,10 +312,19 @@ getNamedVariableListsDomainSpecific(MmsServerConnection connection, char* domain MmsDomain* domain = MmsDevice_getDomain(device, domainName); - if (domain != NULL) { - LinkedList variableLists = MmsDomain_getNamedVariableLists(domain); + if (domain) { + + bool allowAccess = true; - nameList = createStringsFromNamedVariableList(variableLists); + if (connection->server->getNameListHandler) { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection); + } + + if (allowAccess) { + LinkedList variableLists = MmsDomain_getNamedVariableLists(domain); + + nameList = createStringsFromNamedVariableList(variableLists); + } } return nameList; @@ -613,38 +641,79 @@ mmsServer_handleGetNameListRequest( if (objectClass == OBJECT_CLASS_DOMAIN) { - LinkedList nameList = getDomainNames(connection); + bool allowAccess = true; + + if (connection->server->getNameListHandler) { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DOMAINS, NULL, connection); + } + + if (allowAccess) { + LinkedList nameList = getDomainNames(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); - LinkedList_destroyStatic(nameList); + LinkedList_destroyStatic(nameList); + } + else { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { - LinkedList nameList = getNameListVMDSpecific(connection); - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + bool allowAccess = true; + + if (connection->server->getNameListHandler) { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, NULL, connection); + } + + if (allowAccess) { + LinkedList nameList = getNameListVMDSpecific(connection); + +#if (CONFIG_MMS_SORT_NAME_LIST == 1) + StringUtils_sortList(nameList); +#endif + + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); - LinkedList_destroyStatic(nameList); + LinkedList_destroyStatic(nameList); + } + else { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ #if (MMS_DATA_SET_SERVICE == 1) else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { - LinkedList nameList = getNamedVariableListsVMDSpecific(connection); + + bool allowAccess = true; + + if (connection->server->getNameListHandler) { + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, NULL, connection); + } + + if (allowAccess) { + + LinkedList nameList = getNamedVariableListsVMDSpecific(connection); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - StringUtils_sortList(nameList); + StringUtils_sortList(nameList); #endif - createNameListResponse(connection, invokeId, nameList, response, continueAfterId); + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); - LinkedList_destroy(nameList); + LinkedList_destroy(nameList); + + } + else { + mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_DENIED); + } } #endif /* (MMS_DATA_SET_SERVICE == 1) */ diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index 9a203713..b9d4c798 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -372,6 +372,13 @@ MmsServer_installReadJournalHandler(MmsServer self, MmsReadJournalHandler handle self->readJournalHandlerParameter = parameter; } +void +MmsServer_installGetNameListHandler(MmsServer self, MmsGetNameListHandler handler, void* parameter) +{ + self->getNameListHandler = handler; + self->getNameListHandlerParameter = parameter; +} + void MmsServer_setClientAuthenticator(MmsServer self, AcseAuthenticator authenticator, void* authenticatorParameter) { From 5b34251030f21534e43a3368537255172e65a0dc Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 21 Mar 2023 21:59:41 +0000 Subject: [PATCH 039/214] - IED server: fixed - segmentation fault when compiled with CONFIG_MMS_THREADLESS_STACK (LIB61850-398) --- src/iec61850/inc_private/mms_mapping_internal.h | 3 +++ src/iec61850/server/impl/ied_server.c | 8 ++++++++ src/iec61850/server/mms_mapping/mms_mapping.c | 8 ++++++++ src/iec61850/server/mms_mapping/reporting.c | 4 ++++ 4 files changed, 23 insertions(+) diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 3e896495..95727449 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -325,7 +325,10 @@ struct sMmsMapping { /* flag indicates if data model is locked --> prevents reports to be sent */ bool isModelLocked; + +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore isModelLockedMutex; +#endif /* (CONFIG_MMS_THREADLESS_STACK != 1) */ IedServer iedServer; diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index c543a41d..8cc38c93 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -911,11 +911,15 @@ IedServer_lockDataModel(IedServer self) { MmsServer_lockModel(self->mmsServer); +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->mmsMapping->isModelLockedMutex); +#endif self->mmsMapping->isModelLocked = true; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->mmsMapping->isModelLockedMutex); +#endif } void @@ -929,13 +933,17 @@ IedServer_unlockDataModel(IedServer self) /* check if reports have to be sent! */ Reporting_processReportEventsAfterUnlock(self->mmsMapping); +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->mmsMapping->isModelLockedMutex); +#endif MmsServer_unlockModel(self->mmsServer); self->mmsMapping->isModelLocked = false; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->mmsMapping->isModelLockedMutex); +#endif } #if (CONFIG_IEC61850_CONTROL_SERVICE == 1) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index fc080ea2..62dae088 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3721,7 +3721,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) { LinkedList element = self->reportControls; +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->isModelLockedMutex); +#endif bool modelLocked = self->isModelLocked; @@ -3759,7 +3761,9 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) Reporting_processReportEventsAfterUnlock(self); } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->isModelLockedMutex); +#endif } #endif /* (CONFIG_IEC61850_REPORT_SERVICE == 1) */ @@ -3780,13 +3784,17 @@ MmsMapping_triggerGooseObservers(MmsMapping* self, MmsValue* value) if (DataSet_isMemberValue(dataSet, value, NULL)) { MmsGooseControlBlock_setStateChangePending(gcb); +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->isModelLockedMutex); +#endif if (self->isModelLocked == false) { MmsGooseControlBlock_publishNewState(gcb); } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->isModelLockedMutex); +#endif } } } diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 3b170a54..2c5b7572 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -3891,7 +3891,9 @@ processEventsForReport(ReportControl* rc, uint64_t currentTimeInMs) void Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) { +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(self->isModelLockedMutex); +#endif if (self->isModelLocked == false) { @@ -3908,7 +3910,9 @@ Reporting_processReportEvents(MmsMapping* self, uint64_t currentTimeInMs) } } +#if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_post(self->isModelLockedMutex); +#endif } /* From e9298679642f41972ae72c6cfe0bd8c0999ca040 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 7 Apr 2023 08:09:25 +0100 Subject: [PATCH 040/214] - IED server: fixed problem with implicit ResvTms setting when reserved with RptEna (LIB61850-400) --- src/iec61850/server/mms_mapping/reporting.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 6eb6bcee..ba84d98f 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -2011,6 +2011,9 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme if (updateReportDataset(self, rc, NULL, connection)) { if (rc->reserved == false) { + + rc->resvTms = RESV_TMS_IMPLICIT_VALUE; + reserveRcb(rc, connection); if (self->rcbEventHandler) { @@ -2547,7 +2550,6 @@ exit_function: } } - } else if (rc->resvTms == -1) { if (rc->reserved == false) { From 4cba8ab50dbfd0fe06876df9d9a6804e3e852b99 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 13 Apr 2023 23:06:11 +0100 Subject: [PATCH 041/214] - Added function IedConnection_setLocalAddress to define local IP address and optionally local port of a client connection (LIB61850-378) --- dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs | 24 ++++++++++++++++- .../client_example1.c | 27 +++++++++++++++---- src/iec61850/client/ied_connection.c | 9 +++++++ src/iec61850/inc/iec61850_client.h | 11 ++++++++ src/mms/inc/iso_connection_parameters.h | 17 ++++++++++++ src/mms/iso_client/iso_client_connection.c | 11 +++++--- .../iso_common/iso_connection_parameters.c | 12 +++++++++ 7 files changed, 102 insertions(+), 9 deletions(-) diff --git a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs index 4b45fe54..16d79a56 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ClientAPI.cs @@ -1,7 +1,7 @@ /* * IEC61850ClientAPI.cs * - * Copyright 2014-2021 Michael Zillgith + * Copyright 2014-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -437,6 +437,9 @@ namespace IEC61850 [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_connect(IntPtr self, out int error, string hostname, int tcpPort); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] + static extern void IedConnection_setLocalAddress(IntPtr self, string localIpAddress, int localPort); + [DllImport("iec61850", CallingConvention = CallingConvention.Cdecl)] static extern void IedConnection_abort(IntPtr self, out int error); @@ -894,6 +897,25 @@ namespace IEC61850 Connect(hostname, -1); } + /// + /// Set the local IP address and port to be used by the client + /// + /// the local IP address or hostname + /// the local TCP port to use. When 0 the OS will chose the TCP port to use. + public void SetLocalAddress(string localIpAddress, int localPort) + { + IedConnection_setLocalAddress(connection, localIpAddress, localPort); + } + + /// + /// Set the local IP address to be used by the client + /// + /// the local IP address or hostname + public void SetLocalAddress(string localIpAddress) + { + IedConnection_setLocalAddress(connection, localIpAddress, 0); + } + /// This exception is thrown if there is a connection or service error public ControlObject CreateControlObject(string objectReference) { diff --git a/examples/iec61850_client_example1/client_example1.c b/examples/iec61850_client_example1/client_example1.c index f84d37a0..6c9241f0 100644 --- a/examples/iec61850_client_example1/client_example1.c +++ b/examples/iec61850_client_example1/client_example1.c @@ -33,6 +33,9 @@ int main(int argc, char** argv) { char* hostname; int tcpPort = 102; + const char* localIp = NULL; + int localTcpPort = -1; + if (argc > 1) hostname = argv[1]; @@ -42,19 +45,34 @@ int main(int argc, char** argv) { if (argc > 2) tcpPort = atoi(argv[2]); + if (argc > 3) + localIp = argv[3]; + + if (argc > 4) + localTcpPort = atoi(argv[4]); + IedClientError error; IedConnection con = IedConnection_create(); + /* Optional bind to local IP address/interface */ + if (localIp) { + IedConnection_setLocalAddress(con, localIp, localTcpPort); + printf("Bound to Local Address: %s:%i\n", localIp, localTcpPort); + } + IedConnection_connect(con, &error, hostname, tcpPort); + printf("Connecting to %s:%i\n", hostname, tcpPort); - if (error == IED_ERROR_OK) { + if (error == IED_ERROR_OK) + { + printf("Connected\n"); /* read an analog measurement value from server */ MmsValue* value = IedConnection_readObject(con, &error, "simpleIOGenericIO/GGIO1.AnIn1.mag.f", IEC61850_FC_MX); - if (value != NULL) { - + if (value != NULL) + { if (MmsValue_getType(value) == MMS_FLOAT) { float fval = MmsValue_toFloat(value); printf("read float value: %f\n", fval); @@ -137,7 +155,6 @@ close_connection: } IedConnection_destroy(con); + return 0; } - - diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index b49a1fe7..bc581dea 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -651,6 +651,15 @@ IedConnection_tick(IedConnection self) return MmsConnection_tick(self->connection); } +void +IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort) +{ + MmsConnection connection = self->connection; + IsoConnectionParameters isoP = MmsConnection_getIsoConnectionParameters(connection); + + IsoConnectionParameters_setLocalTcpParameters(isoP, localIpAddress, localPort); +} + void IedConnection_setConnectTimeout(IedConnection self, uint32_t timeoutInMs) { diff --git a/src/iec61850/inc/iec61850_client.h b/src/iec61850/inc/iec61850_client.h index 852f2192..c3a174fe 100644 --- a/src/iec61850/inc/iec61850_client.h +++ b/src/iec61850/inc/iec61850_client.h @@ -231,6 +231,17 @@ IedConnection_createWithTlsSupport(TLSConfiguration tlsConfig); LIB61850_API void IedConnection_destroy(IedConnection self); +/** +* \brief Set the local IP address and port to be used by the client +* +* NOTE: This function is optional. When not used the OS decides what IP address and TCP port to use. +* +* \param self IedConnection instance +* \param localIpAddress the local IP address or hostname as C string +* \param localPort the local TCP port to use. When < 1 the OS will chose the TCP port to use. +*/ +LIB61850_API void +IedConnection_setLocalAddress(IedConnection self, const char* localIpAddress, int localPort); /** * \brief set the connect timeout in ms diff --git a/src/mms/inc/iso_connection_parameters.h b/src/mms/inc/iso_connection_parameters.h index e3af848f..df413579 100644 --- a/src/mms/inc/iso_connection_parameters.h +++ b/src/mms/inc/iso_connection_parameters.h @@ -149,6 +149,9 @@ struct sIsoConnectionParameters const char* hostname; int tcpPort; + const char* localIpAddress; + int localTcpPort; + uint8_t remoteApTitle[10]; int remoteApTitleLen; int remoteAEQualifier; @@ -219,6 +222,20 @@ IsoConnectionParameters_setAcseAuthenticationParameter(IsoConnectionParameters s LIB61850_API void IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const char* hostname, int tcpPort); +/** +* \brief Set Local TCP parameters (FOR LIBRARY INTERNAL USE) +* +* NOTE: This function used internally by the MMS Client library. When using the MMS or IEC 61850 API +* there should be no reason for the user to call this function +* +* \param self the IsoConnectionParameters instance +* \param localIpAddress the hostname of local IP address of the server +* \param localTcpPort the local TCP port number of the server +*/ +LIB61850_API void +IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort); + + /** * \brief set the remote AP-Title and AE-Qualifier * diff --git a/src/mms/iso_client/iso_client_connection.c b/src/mms/iso_client/iso_client_connection.c index cb58e98f..4f37176b 100644 --- a/src/mms/iso_client/iso_client_connection.c +++ b/src/mms/iso_client/iso_client_connection.c @@ -692,8 +692,13 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim /* set timeout for connect */ self->nextReadTimeout = Hal_getTimeInMs() + connectTimeoutInMs; + /* Connect to Local Ip Address*/ + if (self->parameters->localIpAddress) { + Socket_bind(self->socket, self->parameters->localIpAddress, self->parameters->localTcpPort); + } + if (Socket_connectAsync(self->socket, self->parameters->hostname, self->parameters->tcpPort) == false) { - + Socket_destroy(self->socket); self->socket = NULL; @@ -704,9 +709,9 @@ IsoClientConnection_associateAsync(IsoClientConnection self, uint32_t connectTim success = false; } - + Semaphore_post(self->tickMutex); - + return success; } diff --git a/src/mms/iso_common/iso_connection_parameters.c b/src/mms/iso_common/iso_connection_parameters.c index 3fdf32c9..aa5334b3 100644 --- a/src/mms/iso_common/iso_connection_parameters.c +++ b/src/mms/iso_common/iso_connection_parameters.c @@ -104,6 +104,18 @@ IsoConnectionParameters_setTcpParameters(IsoConnectionParameters self, const cha self->tcpPort = tcpPort; } +void +IsoConnectionParameters_setLocalTcpParameters(IsoConnectionParameters self, const char* localIpAddress, int localTcpPort) +{ + if (self) { + if (localIpAddress) { + self->localIpAddress = strdup(localIpAddress); + self->localTcpPort = localTcpPort; + } + } +} + + void IsoConnectionParameters_setRemoteApTitle(IsoConnectionParameters self, const char* apTitle, int aeQualifier) { From 24babeb2f9ad8276aecf581edce6786d9aa7217f Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 2 May 2023 19:17:25 +0100 Subject: [PATCH 042/214] - GOOSE subscriber: fixed - possible heap corruption in parseAllData due to missing validity check in bit-string handling (LIB61850-402) --- hal/ethernet/linux/ethernet_linux.c | 2 ++ src/common/string_utilities.c | 3 +++ src/goose/goose_receiver.c | 42 ++++++++++++++++++++++------- src/goose/goose_subscriber.h | 1 + src/mms/iso_mms/common/mms_value.c | 10 ++++--- 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/hal/ethernet/linux/ethernet_linux.c b/hal/ethernet/linux/ethernet_linux.c index 2d97a826..eaf1897a 100644 --- a/hal/ethernet/linux/ethernet_linux.c +++ b/hal/ethernet/linux/ethernet_linux.c @@ -263,6 +263,8 @@ void Ethernet_addMulticastAddress(EthernetSocket ethSocket, uint8_t* multicastAddress) { struct packet_mreq mreq; + memset(&mreq, 0, sizeof(struct packet_mreq)); + mreq.mr_ifindex = ethSocket->socketAddress.sll_ifindex; mreq.mr_alen = ETH_ALEN; mreq.mr_type = PACKET_MR_MULTICAST; diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c index 41fd1192..abf9a04c 100644 --- a/src/common/string_utilities.c +++ b/src/common/string_utilities.c @@ -197,6 +197,9 @@ StringUtils_copyStringMax(char* dest, int maxBufferSize, const char* str1) { char* res = dest; + if (maxBufferSize < 1) + return NULL; + if (dest == NULL) res = (char*)GLOBAL_MALLOC(maxBufferSize); diff --git a/src/goose/goose_receiver.c b/src/goose/goose_receiver.c index 06160008..63fe9710 100644 --- a/src/goose/goose_receiver.c +++ b/src/goose/goose_receiver.c @@ -210,13 +210,22 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case 0x84: /* BIT STRING */ if (MmsValue_getType(value) == MMS_BIT_STRING) { int padding = buffer[bufPos]; - int bitStringLength = (8 * (elementLength - 1)) - padding; - if (bitStringLength == value->value.bitString.size) { - memcpy(value->value.bitString.buf, buffer + bufPos + 1, - elementLength - 1); + + if (padding > 7) { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: invalid bit-string (padding not plausible)\n"); + + pe = GOOSE_PARSE_ERROR_INVALID_PADDING; } else { - pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH; + int bitStringLength = (8 * (elementLength - 1)) - padding; + if (bitStringLength == value->value.bitString.size) { + memcpy(value->value.bitString.buf, buffer + bufPos + 1, + elementLength - 1); + } + else { + pe = GOOSE_PARSE_ERROR_LENGTH_MISMATCH; + } } } else { @@ -352,7 +361,7 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) break; } - if ( pe != GOOSE_PARSE_ERROR_NO_ERROR ) { + if (pe != GOOSE_PARSE_ERROR_NO_ERROR) { break; /* from while */ } @@ -362,14 +371,16 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) } if (elementIndex <= maxIndex) { - pe = GOOSE_PARSE_ERROR_UNDERFLOW; + if (pe == GOOSE_PARSE_ERROR_NO_ERROR) { + pe = GOOSE_PARSE_ERROR_UNDERFLOW; + } } if (DEBUG_GOOSE_SUBSCRIBER) { - switch ( pe ) { + switch (pe) { case GOOSE_PARSE_ERROR_UNKNOWN_TAG: printf("GOOSE_SUBSCRIBER: Found unkown tag %02x!\n", tag); - break; + break; case GOOSE_PARSE_ERROR_TAGDECODE: printf("GOOSE_SUBSCRIBER: Malformed message: failed to decode BER length tag!\n"); break; @@ -388,6 +399,8 @@ parseAllData(uint8_t* buffer, int allDataLength, MmsValue* dataSetValues) case GOOSE_PARSE_ERROR_LENGTH_MISMATCH: printf("GOOSE_SUBSCRIBER: Message contains value of wrong length!\n"); break; + case GOOSE_PARSE_ERROR_INVALID_PADDING: + printf("GOOSE_SUBSCRIBER: Malformed message: invalid padding!\n"); default: break; } @@ -500,7 +513,16 @@ parseAllDataUnknownValue(GooseSubscriber self, uint8_t* buffer, int allDataLengt case 0x83: /* boolean */ if (DEBUG_GOOSE_SUBSCRIBER) printf("GOOSE_SUBSCRIBER: found boolean\n"); - value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos)); + + if (elementLength > 0) { + value = MmsValue_newBoolean(BerDecoder_decodeBoolean(buffer, bufPos)); + } + else { + if (DEBUG_GOOSE_SUBSCRIBER) + printf("GOOSE_SUBSCRIBER: invalid length for boolean\n"); + + goto exit_with_error; + } break; diff --git a/src/goose/goose_subscriber.h b/src/goose/goose_subscriber.h index e58f4f96..7408d6b5 100644 --- a/src/goose/goose_subscriber.h +++ b/src/goose/goose_subscriber.h @@ -47,6 +47,7 @@ typedef enum GOOSE_PARSE_ERROR_UNDERFLOW, GOOSE_PARSE_ERROR_TYPE_MISMATCH, GOOSE_PARSE_ERROR_LENGTH_MISMATCH, + GOOSE_PARSE_ERROR_INVALID_PADDING } GooseParseError; typedef struct sGooseSubscriber* GooseSubscriber; diff --git a/src/mms/iso_mms/common/mms_value.c b/src/mms/iso_mms/common/mms_value.c index 29e96e4d..549619a8 100644 --- a/src/mms/iso_mms/common/mms_value.c +++ b/src/mms/iso_mms/common/mms_value.c @@ -2205,7 +2205,7 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) const char* currentStr = MmsValue_printToBuffer((const MmsValue*) MmsValue_getElement(self, i), buffer + bufPos, bufferSize - bufPos); - bufPos += strlen(currentStr); + bufPos += strnlen(currentStr, bufferSize - bufPos); if (bufPos >= bufferSize) break; @@ -2240,9 +2240,13 @@ MmsValue_printToBuffer(const MmsValue* self, char* buffer, int bufferSize) int size = MmsValue_getBitStringSize(self); /* fill buffer with zeros */ - if (size > bufferSize) { + if (size + 1 > bufferSize) { memset(buffer, 0, bufferSize); - break; + + size = bufferSize - 1; + + if (size < 1) + break; } int i; From d129b8458a29a618b77aa42c804bba06be8061a5 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 16 May 2023 19:12:51 +0100 Subject: [PATCH 043/214] - make write access to RCB elements configurable according to ReportSettings (LIB61850-404) --- src/iec61850/inc/iec61850_server.h | 31 +++++++++++++++ src/iec61850/inc_private/ied_server_private.h | 3 +- src/iec61850/server/impl/ied_server.c | 7 ++++ src/iec61850/server/impl/ied_server_config.c | 39 ++++++++++++++++++- src/iec61850/server/mms_mapping/reporting.c | 38 ++++++++++++++++++ 5 files changed, 116 insertions(+), 2 deletions(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 07f3874f..3cb5eded 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -42,6 +42,13 @@ extern "C" { #include "iso_connection_parameters.h" #include "iec61850_config_file_parser.h" +#define IEC61850_REPORTSETTINGS_RPT_ID 1 +#define IEC61850_REPORTSETTINGS_BUF_TIME 2 +#define IEC61850_REPORTSETTINGS_DATSET 4 +#define IEC61850_REPORTSETTINGS_TRG_OPS 8 +#define IEC61850_REPORTSETTINGS_OPT_FIELDS 16 +#define IEC61850_REPORTSETTINGS_INTG_PD 32 + /** * \brief Configuration object to configure IEC 61850 stack features */ @@ -99,6 +106,9 @@ struct sIedServerConfig /** integrity report start times will by synchronized with straight numbers (default: false) */ bool syncIntegrityReportTimes; + + /** for each configurable ReportSetting there is a separate flag (default: Dyn = enable write for all) */ + uint8_t reportSettingsWritable; }; /** @@ -388,6 +398,27 @@ IedServerConfig_useIntegratedGoosePublisher(IedServerConfig self, bool enable); LIB61850_API bool IedServerConfig_isLogServiceEnabled(IedServerConfig self); +/** + * \brief Make a configurable report setting writeable or read-only + * + * \note Can be used to implement some of Services\ReportSettings options + * + * \param[in] setting one 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 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 */ diff --git a/src/iec61850/inc_private/ied_server_private.h b/src/iec61850/inc_private/ied_server_private.h index e6f6e412..d5555659 100644 --- a/src/iec61850/inc_private/ied_server_private.h +++ b/src/iec61850/inc_private/ied_server_private.h @@ -3,7 +3,7 @@ * * Library private function definitions for IedServer. * - * Copyright 2013-2018 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -50,6 +50,7 @@ struct sIedServer bool enableBRCBResvTms; bool enableOwnerForRCB; bool syncIntegrityReportTimes; + uint8_t rcbSettingsWritable; #endif #if (CONFIG_MMS_THREADLESS_STACK != 1) diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 96f300ec..27d40624 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -580,12 +580,19 @@ IedServer_createWithConfig(IedModel* dataModel, TLSConfiguration tlsConfiguratio self->enableBRCBResvTms = serverConfiguration->enableResvTmsForBRCB; self->enableOwnerForRCB = serverConfiguration->enableOwnerForRCB; self->syncIntegrityReportTimes = serverConfiguration->syncIntegrityReportTimes; + self->rcbSettingsWritable = serverConfiguration->reportSettingsWritable; } else { self->reportBufferSizeBRCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->reportBufferSizeURCBs = CONFIG_REPORTING_DEFAULT_REPORT_BUFFER_SIZE; self->enableOwnerForRCB = false; self->syncIntegrityReportTimes = false; + self->rcbSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID + + IEC61850_REPORTSETTINGS_BUF_TIME + + IEC61850_REPORTSETTINGS_DATSET + + IEC61850_REPORTSETTINGS_TRG_OPS + + IEC61850_REPORTSETTINGS_OPT_FIELDS + + IEC61850_REPORTSETTINGS_INTG_PD; #if (CONFIG_IEC61850_BRCB_WITH_RESVTMS == 1) self->enableBRCBResvTms = true; #else diff --git a/src/iec61850/server/impl/ied_server_config.c b/src/iec61850/server/impl/ied_server_config.c index fdc38f8e..673f58b1 100644 --- a/src/iec61850/server/impl/ied_server_config.c +++ b/src/iec61850/server/impl/ied_server_config.c @@ -1,7 +1,7 @@ /* * ied_server_config.c * - * Copyright 2018-2022 Michael Zillgith + * Copyright 2018-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -59,6 +59,12 @@ IedServerConfig_create() self->enableResvTmsForBRCB = true; self->enableOwnerForRCB = false; self->syncIntegrityReportTimes = false; + self->reportSettingsWritable = IEC61850_REPORTSETTINGS_RPT_ID + + IEC61850_REPORTSETTINGS_BUF_TIME + + IEC61850_REPORTSETTINGS_DATSET + + IEC61850_REPORTSETTINGS_TRG_OPS + + IEC61850_REPORTSETTINGS_OPT_FIELDS + + IEC61850_REPORTSETTINGS_INTG_PD; } return self; @@ -264,3 +270,34 @@ IedServerConfig_getSyncIntegrityReportTimes(IedServerConfig self) { return self->syncIntegrityReportTimes; } + +static void +configureSetting(IedServerConfig self, uint8_t flags, uint8_t setting, bool value) +{ + if (flags & setting) + { + if (value) { + self->reportSettingsWritable |= setting; + } + else { + self->reportSettingsWritable &= ~setting; + } + } +} + +void +IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isDyn) +{ + configureSetting(self, setting, IEC61850_REPORTSETTINGS_RPT_ID, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_BUF_TIME, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_DATSET, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_TRG_OPS, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_OPT_FIELDS, isDyn); + configureSetting(self, setting, IEC61850_REPORTSETTINGS_INTG_PD, isDyn); +} + +bool +IedServerConfig_getReportSetting(IedServerConfig self, uint8_t setting) +{ + return (self->reportSettingsWritable & setting); +} diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index ba84d98f..393c8979 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -2193,6 +2193,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } else if (strcmp(elementName, "DatSet") == 0) { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_DATSET)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -2240,6 +2246,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } else if (strcmp(elementName, "IntgPd") == 0) { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_INTG_PD)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -2287,6 +2299,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } else if (strcmp(elementName, "TrgOps") == 0) { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_TRG_OPS)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -2360,6 +2378,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme else if (strcmp(elementName, "BufTm") == 0) { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_BUF_TIME)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -2393,6 +2417,12 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme } else if (strcmp(elementName, "RptID") == 0) { + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_RPT_ID)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); #endif @@ -2500,6 +2530,14 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme goto exit_function; } + else if (strcmp(elementName, "OptFlds") == 0) { + + if (!(self->iedServer->rcbSettingsWritable & IEC61850_REPORTSETTINGS_OPT_FIELDS)) + { + retVal = DATA_ACCESS_ERROR_OBJECT_ACCESS_DENIED; + goto exit_function; + } + } #if (CONFIG_MMS_THREADLESS_STACK != 1) Semaphore_wait(rc->rcbValuesLock); From 9db516350b6bfea37e2751254b5542fdb08c81d4 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 17 May 2023 07:35:24 +0100 Subject: [PATCH 044/214] - updated doc comment --- src/iec61850/inc/iec61850_server.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 3cb5eded..3db3235e 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -403,7 +403,7 @@ IedServerConfig_isLogServiceEnabled(IedServerConfig self); * * \note Can be used to implement some of Services\ReportSettings options * - * \param[in] setting one IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD + * \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 @@ -412,7 +412,7 @@ IedServerConfig_setReportSetting(IedServerConfig self, uint8_t setting, bool isD /** * \brief Check if a configurable report setting is writable or read-only * - * \param[in] setting one IEC61850_REPORTSETTINGS_RPT_ID, _BUF_TIME, _DATSET, _TRG_OPS, _OPT_FIELDS, _INTG_PD + * \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 */ From c9d9271a5263e8b125bcbd78e4ec2635ef760afb Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 29 May 2023 15:40:13 +0100 Subject: [PATCH 045/214] - fixed - arrays of complex data attributes not handled correctly (LIB61850-408) --- src/iec61850/server/mms_mapping/mms_mapping.c | 5 +++-- src/iec61850/server/model/dynamic_model.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 813f9566..8e63e66c 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -139,7 +139,7 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute) sizeof(MmsVariableSpecification)); namedVariable = namedVariable->typeSpec.array.elementTypeSpec; - if (attribute->firstChild && ((DataAttribute*)(attribute->firstChild))->type != IEC61850_CONSTRUCTED) { + if (attribute->type != IEC61850_CONSTRUCTED) { isBasicArray = true; } } @@ -160,7 +160,8 @@ createNamedVariableFromDataAttribute(DataAttribute* attribute) DataAttribute* subDataAttribute = (DataAttribute*) attribute->firstChild; int i = 0; - while (subDataAttribute != NULL) { + while (subDataAttribute) + { namedVariable->typeSpec.structure.elements[i] = createNamedVariableFromDataAttribute(subDataAttribute); diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 9b0a53fd..790c9be3 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -688,7 +688,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type self->triggerOptions = triggerOptions; self->sAddr = sAddr; - if ((arrayElements > 0) && (type != IEC61850_CONSTRUCTED)) { + if (arrayElements > 0) { int i; for (i = 0; i < arrayElements; i++) { @@ -703,7 +703,7 @@ DataAttribute_create(const char* name, ModelNode* parent, DataAttributeType type arrayElement->fc = fc; arrayElement->firstChild = NULL; arrayElement->mmsValue = NULL; - arrayElement->parent = parent; + arrayElement->parent = (ModelNode*)self; arrayElement->sibling = NULL; arrayElement->triggerOptions = triggerOptions; arrayElement->sAddr = sAddr; From a5f5597980cf66466a3069d53c82bf93787721a3 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 30 May 2023 08:09:24 +0100 Subject: [PATCH 046/214] - fix potential crash in ModelNode_getChild when an array element field is accessed without using the array index specifier (LIB61850-408) --- src/iec61850/server/model/model.c | 150 +++++++++++++++--------------- 1 file changed, 77 insertions(+), 73 deletions(-) diff --git a/src/iec61850/server/model/model.c b/src/iec61850/server/model/model.c index 9d660ba5..375d62f9 100644 --- a/src/iec61850/server/model/model.c +++ b/src/iec61850/server/model/model.c @@ -649,106 +649,110 @@ ModelNode_getChildCount(ModelNode* modelNode) { ModelNode* ModelNode_getChild(ModelNode* self, const char* name) { - /* check for element separator */ - const char* separator = strchr(name, '.'); + /* check for element separator */ + const char* separator = strchr(name, '.'); - /* allow first character to be "." */ - if (separator == name) - name++; + /* allow first character to be "." */ + if (separator == name) + name++; - /* check for array separator */ - const char* arraySeparator = strchr(name, '('); + /* check for array separator */ + const char* arraySeparator = strchr(name, '('); - if (arraySeparator) { + if (arraySeparator) { - const char* arraySeparator2 = strchr(arraySeparator, ')'); + const char* arraySeparator2 = strchr(arraySeparator, ')'); - if (arraySeparator2) { - int idx = (int) strtol(arraySeparator + 1, NULL, 10); + if (arraySeparator2) { + int idx = (int) strtol(arraySeparator + 1, NULL, 10); - ModelNode* arrayNode = NULL; + ModelNode* arrayNode = NULL; - if (name == arraySeparator) { - arrayNode = ModelNode_getChildWithIdx(self, idx); - } - else { - char nameCopy[65]; + if (name == arraySeparator) { + arrayNode = ModelNode_getChildWithIdx(self, idx); + } + else { + char nameCopy[65]; - const char* pos = name; + const char* pos = name; - int cpyIdx = 0; + int cpyIdx = 0; - while (pos < arraySeparator) { - nameCopy[cpyIdx] = *pos; - cpyIdx++; - pos++; - } + while (pos < arraySeparator) { + nameCopy[cpyIdx] = *pos; + cpyIdx++; + pos++; + } - nameCopy[cpyIdx] = 0; + nameCopy[cpyIdx] = 0; - ModelNode* childNode = ModelNode_getChild(self, nameCopy); + ModelNode* childNode = ModelNode_getChild(self, nameCopy); - if (childNode) { - arrayNode = ModelNode_getChildWithIdx(childNode, idx); - } - else - return NULL; - } + if (childNode) { + arrayNode = ModelNode_getChildWithIdx(childNode, idx); + } + else + return NULL; + } - if (arrayNode) { + if (arrayNode) { - if (*(arraySeparator2 + 1) == 0) { - return arrayNode; - } - else { - if (*(arraySeparator2 + 1) == '.') - return ModelNode_getChild(arrayNode, arraySeparator2 + 2); - else - return ModelNode_getChild(arrayNode, arraySeparator2 + 1); - } + if (*(arraySeparator2 + 1) == 0) { + return arrayNode; + } + else { + if (*(arraySeparator2 + 1) == '.') + return ModelNode_getChild(arrayNode, arraySeparator2 + 2); + else + return ModelNode_getChild(arrayNode, arraySeparator2 + 1); + } - } - else - return NULL; + } + else + return NULL; + } + else { + /* invalid name */ + return NULL; + } - } - else { - /* invalid name */ - return NULL; - } + } - } + int nameElementLength = 0; - int nameElementLength = 0; + if (separator != NULL) + nameElementLength = (separator - name); + else + nameElementLength = strlen(name); - if (separator != NULL) - nameElementLength = (separator - name); - else - nameElementLength = strlen(name); + ModelNode* nextNode = self->firstChild; - ModelNode* nextNode = self->firstChild; + ModelNode* matchingNode = NULL; - ModelNode* matchingNode = NULL; + while (nextNode) { - while (nextNode) { - int nodeNameLen = strlen(nextNode->name); + if (nextNode->name == NULL) { + break; /* is an array element */ + } - if (nodeNameLen == nameElementLength) { + int nodeNameLen = strlen(nextNode->name); - if (memcmp(nextNode->name, name, nodeNameLen) == 0) { - matchingNode = nextNode; - break; - } - } + if (nodeNameLen == nameElementLength) { - nextNode = nextNode->sibling; - } + if (memcmp(nextNode->name, name, nodeNameLen) == 0) { + matchingNode = nextNode; + break; + } + } - if ((separator != NULL) && (matchingNode != NULL)) { - return ModelNode_getChild(matchingNode, separator + 1); - } - else - return matchingNode; + nextNode = nextNode->sibling; + } + + if ((separator != NULL) && (matchingNode != NULL)) { + return ModelNode_getChild(matchingNode, separator + 1); + } + else + return matchingNode; } ModelNode* From 3036f99f8f07028cc097621e4a28564715e45818 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 1 Jun 2023 19:57:09 +0200 Subject: [PATCH 047/214] - .NET API: Save TLSConfiguration instance in IedServer to avoid garbage collection (LIB61850-410) --- dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs index f9588668..b0d2c054 100644 --- a/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs +++ b/dotnet/IEC61850forCSharp/IEC61850ServerAPI.cs @@ -2456,6 +2456,9 @@ namespace IEC61850 /* store IedModel instance to prevent garbage collector */ private IedModel iedModel = null; + /* store TLSConfiguration instance to prevent garbage collector */ + private TLSConfiguration tlsConfiguration = null; + public IedServer(IedModel iedModel, IedServerConfig config = null) { this.iedModel = iedModel; @@ -2471,6 +2474,7 @@ namespace IEC61850 public IedServer(IedModel iedModel, TLSConfiguration tlsConfig, IedServerConfig config = null) { this.iedModel = iedModel; + this.tlsConfiguration = tlsConfig; IntPtr nativeConfig = IntPtr.Zero; IntPtr nativeTLSConfig = IntPtr.Zero; @@ -2560,6 +2564,7 @@ namespace IEC61850 self = IntPtr.Zero; internalConnectionHandler = null; this.iedModel = null; + this.tlsConfiguration = null; } } } From c7528975dc93164d57340797205f952025551b17 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 6 Jun 2023 11:16:53 +0100 Subject: [PATCH 048/214] - fixed - server sends dchg report when only dupd is enabled in RCB (LIB61850-411) --- src/iec61850/server/mms_mapping/mms_mapping.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 8e63e66c..2d76f39c 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3881,8 +3881,7 @@ MmsMapping_triggerReportObservers(MmsMapping* self, MmsValue* value, int flag) continue; break; case REPORT_CONTROL_VALUE_CHANGED: - if (((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) && - ((rc->triggerOps & TRG_OPT_DATA_UPDATE) == 0)) + if ((rc->triggerOps & TRG_OPT_DATA_CHANGED) == 0) continue; break; case REPORT_CONTROL_QUALITY_CHANGED: From 9404622c5afca4dbb0c7e0b977c3c7ec2b9e0e81 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 9 Jun 2023 15:50:39 +0100 Subject: [PATCH 049/214] - IEDConnection: check for index out of bounds in ClientReport_getReasonForInclusion --- src/iec61850/client/client_report.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iec61850/client/client_report.c b/src/iec61850/client/client_report.c index fbaa74c2..af12d26a 100644 --- a/src/iec61850/client/client_report.c +++ b/src/iec61850/client/client_report.c @@ -139,7 +139,7 @@ ClientReport_getRptId(ClientReport self) ReasonForInclusion ClientReport_getReasonForInclusion(ClientReport self, int elementIndex) { - if (self->reasonForInclusion != NULL) + if ((self->reasonForInclusion != NULL) && (elementIndex < self->dataSetSize) && (elementIndex >= 0)) return self->reasonForInclusion[elementIndex]; else return IEC61850_REASON_NOT_INCLUDED; From 7c80c9562927c3209b6b5abe73feb0be93e3f8ce Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 9 Jun 2023 16:22:54 +0100 Subject: [PATCH 050/214] - fixed - Memory leak in RCB handling code when data set is updated (LIB61850-412) --- src/iec61850/server/mms_mapping/reporting.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index 393c8979..9cbbb4f2 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -717,6 +717,8 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, MmsValue* dataSetValue; + bool isUsedDataSetDynamic = rc->isDynamicDataSet; + if (newDatSet != NULL) { if (strcmp(MmsValue_toString(newDatSet), "") == 0) { success = true; @@ -788,15 +790,6 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, } } - if (rc->isDynamicDataSet) { - if (rc->dataSet && dataSetChanged) { - deleteDataSetValuesShadowBuffer(rc); - MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); - rc->isDynamicDataSet = false; - rc->dataSet = NULL; - } - } - if (dataSetValue) { const char* dataSetName = MmsValue_toString(dataSetValue); @@ -877,11 +870,20 @@ updateReportDataset(MmsMapping* mapping, ReportControl* rc, MmsValue* newDatSet, #endif /* (MMS_DYNAMIC_DATA_SETS == 1) */ + if (rc->dataSet && rc->dataSet != dataSet) + dataSetChanged = true; + if (dataSetChanged) { /* delete pending event and create buffer for new data set */ deleteDataSetValuesShadowBuffer(rc); + if (isUsedDataSetDynamic) { + if (rc->dataSet) { + MmsMapping_freeDynamicallyCreatedDataSet(rc->dataSet); + } + } + rc->dataSet = dataSet; createDataSetValuesShadowBuffer(rc); From dcfa56c526743c9885bc3a1614ae34a189aeeaea Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 20 Jun 2023 15:44:51 +0100 Subject: [PATCH 051/214] - MMS client: added function MmsConnection_writeVariableComponent to write to variables with alternate component access (LIB61850-414) --- src/mms/inc/mms_client_connection.h | 22 +++++++ src/mms/inc_private/mms_client_internal.h | 5 ++ .../iso_mms/client/mms_client_connection.c | 63 +++++++++++++++++++ src/mms/iso_mms/client/mms_client_write.c | 57 +++++++++++++++++ 4 files changed, 147 insertions(+) diff --git a/src/mms/inc/mms_client_connection.h b/src/mms/inc/mms_client_connection.h index 70bedc28..86f12c9b 100644 --- a/src/mms/inc/mms_client_connection.h +++ b/src/mms/inc/mms_client_connection.h @@ -648,6 +648,23 @@ MmsConnection_writeVariableAsync(MmsConnection self, uint32_t* usedInvokeId, Mms MmsConnection_WriteVariableHandler handler, void* parameter); +/** + * \brief Write a single variable to the server (using component alternate access) + * + * \param self MmsConnection instance to operate on + * \param mmsError user provided variable to store error code + * \param domainId the domain name of the variable to be written + * \param itemId name of the variable to be written + * \param componentId the name of the variable component + * \param value value of the variable to be written + * + * \return when successful, the data access error value returned by the server + */ +LIB61850_API MmsDataAccessError +MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, + const char* componentId, MmsValue* value); + /** * \brief Write a single array element with a component to an array type variable * @@ -672,6 +689,11 @@ MmsConnection_writeSingleArrayElementWithComponentAsync(MmsConnection self, uint uint32_t arrayIndex, const char* componentId, MmsValue* value, MmsConnection_WriteVariableHandler handler, void* parameter); +LIB61850_API void +MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, + const char* domainId, const char* itemId, const char* componentId, MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter); + /** * \brief Write a single array element or a sub array to an array type variable * diff --git a/src/mms/inc_private/mms_client_internal.h b/src/mms/inc_private/mms_client_internal.h index 57243230..aa0e60f3 100644 --- a/src/mms/inc_private/mms_client_internal.h +++ b/src/mms/inc_private/mms_client_internal.h @@ -272,6 +272,11 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const int startIndex, int elementCount, MmsValue* value, ByteBuffer* writeBuffer); +LIB61850_INTERNAL int +mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, + MmsValue* value, + ByteBuffer* writeBuffer); + LIB61850_INTERNAL int mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t arrayIndex, const char* component, diff --git a/src/mms/iso_mms/client/mms_client_connection.c b/src/mms/iso_mms/client/mms_client_connection.c index 63b50b5f..a9763c08 100644 --- a/src/mms/iso_mms/client/mms_client_connection.c +++ b/src/mms/iso_mms/client/mms_client_connection.c @@ -4379,6 +4379,69 @@ exit_function: return; } +MmsDataAccessError +MmsConnection_writeVariableComponent(MmsConnection self, MmsError* mmsError, + const char* domainId, const char* itemId, + const char* componentId, MmsValue* value) +{ + struct writeVariableParameters parameter; + + MmsError err = MMS_ERROR_NONE; + + parameter.waitForResponse = Semaphore_create(1); + parameter.err = MMS_ERROR_NONE; + parameter.accessError = DATA_ACCESS_ERROR_SUCCESS; + + Semaphore_wait(parameter.waitForResponse); + + MmsConnection_writeVariableComponentAsync(self, NULL, &err, domainId, itemId, componentId, value, writeVariableHandler, ¶meter); + + if (err == MMS_ERROR_NONE) { + Semaphore_wait(parameter.waitForResponse); + + err = parameter.err; + } + + Semaphore_destroy(parameter.waitForResponse); + + if (mmsError) + *mmsError = err; + + return parameter.accessError; +} + +void +MmsConnection_writeVariableComponentAsync(MmsConnection self, uint32_t* usedInvokeId, MmsError* mmsError, + const char* domainId, const char* itemId, const char* componentId, MmsValue* value, + MmsConnection_WriteVariableHandler handler, void* parameter) +{ + if (getConnectionState(self) != MMS_CONNECTION_STATE_CONNECTED) { + if (mmsError) + *mmsError = MMS_ERROR_CONNECTION_LOST; + goto exit_function; + } + + ByteBuffer* payload = IsoClientConnection_allocateTransmitBuffer(self->isoClient); + + uint32_t invokeId = getNextInvokeId(self); + + if (usedInvokeId) + *usedInvokeId = invokeId; + + mmsClient_createWriteRequestComponent(invokeId, domainId, itemId, componentId, value, payload); + + MmsClientInternalParameter intParam; + intParam.ptr = NULL; + + MmsError err = sendAsyncRequest(self, invokeId, payload, MMS_CALL_TYPE_WRITE_VARIABLE, handler, parameter, intParam); + + if (mmsError) + *mmsError = err; + +exit_function: + return; +} + struct writeMultipleVariablesParameter { Semaphore sem; diff --git a/src/mms/iso_mms/client/mms_client_write.c b/src/mms/iso_mms/client/mms_client_write.c index e99a6e1b..fc2b3d62 100644 --- a/src/mms/iso_mms/client/mms_client_write.c +++ b/src/mms/iso_mms/client/mms_client_write.c @@ -529,6 +529,63 @@ mmsClient_createWriteRequestArray(uint32_t invokeId, const char* domainId, const return rval.encoded; } +int +mmsClient_createWriteRequestComponent(uint32_t invokeId, const char* domainId, const char* itemId, const char* component, + MmsValue* value, + ByteBuffer* writeBuffer) +{ + MmsPdu_t* mmsPdu = mmsClient_createConfirmedRequestPdu(invokeId); + + mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.present = + ConfirmedServiceRequest_PR_write; + WriteRequest_t* request = + &(mmsPdu->choice.confirmedRequestPdu.confirmedServiceRequest.choice.write); + + /* Create list of variable specifications */ + request->variableAccessSpecification.present = VariableAccessSpecification_PR_listOfVariable; + request->variableAccessSpecification.choice.listOfVariable.list.count = 1; + request->variableAccessSpecification.choice.listOfVariable.list.array = + (ListOfVariableSeq_t**) GLOBAL_CALLOC(1, sizeof(ListOfVariableSeq_t*)); + + ListOfVariableSeq_t* variableIdentifier = createNewDomainVariableSpecification(domainId, itemId); + + request->variableAccessSpecification.choice.listOfVariable.list.array[0] = variableIdentifier; + + variableIdentifier->alternateAccess = mmsClient_createAlternateAccessComponent(component); + + /* Create list of typed data values */ + request->listOfData.list.count = 1; + request->listOfData.list.size = 1; + request->listOfData.list.array = (Data_t**) GLOBAL_CALLOC(1, sizeof(struct Data*)); + request->listOfData.list.array[0] = mmsMsg_createBasicDataElement(value); + + /* Encode complete ASN1 structure */ + + asn_enc_rval_t rval; + + rval = der_encode(&asn_DEF_MmsPdu, mmsPdu, + (asn_app_consume_bytes_f*) mmsClient_write_out, (void*) writeBuffer); + + /* Free ASN structure */ + mmsClient_deleteAlternateAccess(variableIdentifier->alternateAccess); + request->variableAccessSpecification.choice.listOfVariable.list.count = 0; + + GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array[0]); + GLOBAL_FREEMEM(request->variableAccessSpecification.choice.listOfVariable.list.array); + request->variableAccessSpecification.choice.listOfVariable.list.array = 0; + + request->listOfData.list.count = 0; + + deleteDataElement(request->listOfData.list.array[0]); + + GLOBAL_FREEMEM(request->listOfData.list.array); + request->listOfData.list.array = 0; + + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + + return rval.encoded; +} + int mmsClient_createWriteRequestAlternateAccessSingleIndexComponent(uint32_t invokeId, const char* domainId, const char* itemId, uint32_t arrayIndex, const char* component, From 5c5ceb65b59c7d7679a0d8cb478026a7c3e69e1d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 20 Jun 2023 15:52:55 +0100 Subject: [PATCH 052/214] - MMS server: added support for write access with component alternate access (LIB61850-414) --- src/mms/iso_mms/server/mms_write_service.c | 160 +++++++++++++++------ 1 file changed, 115 insertions(+), 45 deletions(-) diff --git a/src/mms/iso_mms/server/mms_write_service.c b/src/mms/iso_mms/server/mms_write_service.c index 6a9d909f..096bc30f 100644 --- a/src/mms/iso_mms/server/mms_write_service.c +++ b/src/mms/iso_mms/server/mms_write_service.c @@ -512,6 +512,58 @@ handleWriteNamedVariableListRequest( } +static MmsVariableSpecification* +getComponent(MmsServerConnection connection, MmsDomain* domain, AlternateAccess_t* alternateAccess, MmsVariableSpecification* namedVariable, char* variableName) +{ + MmsVariableSpecification* retValue = NULL; + + if (mmsServer_isComponentAccess(alternateAccess)) { + Identifier_t component = + alternateAccess->list.array[0]->choice.unnamed->choice.selectAccess.choice.component; + + if (component.size > 129) + goto exit_function; + + if (namedVariable->type == MMS_STRUCTURE) { + + int i; + + for (i = 0; i < namedVariable->typeSpec.structure.elementCount; i++) { + + if ((int) strlen(namedVariable->typeSpec.structure.elements[i]->name) + == component.size) { + if (!strncmp(namedVariable->typeSpec.structure.elements[i]->name, + (char*) component.buf, component.size)) + { + if (strlen(variableName) + component.size < 199) { + + StringUtils_appendString(variableName, 200, "$"); + + /* here we need strncat because component.buf is not null terminated! */ + strncat(variableName, (const char*)component.buf, (size_t)component.size); + + if (alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess + != NULL) { + retValue = + getComponent(connection, domain, + alternateAccess->list.array[0]->choice.unnamed->choice.selectAlternateAccess.alternateAccess, + namedVariable->typeSpec.structure.elements[i], + variableName); + } + else { + retValue = namedVariable->typeSpec.structure.elements[i]; + } + } + } + } + } + } + } + +exit_function: + return retValue; +} + void mmsServer_handleWriteRequest( MmsServerConnection connection, @@ -636,15 +688,21 @@ mmsServer_handleWriteRequest( AlternateAccess_t* alternateAccess = varSpec->alternateAccess; if (alternateAccess != NULL) { - if (variable->type != MMS_ARRAY) { + + if ((variable->type == MMS_STRUCTURE) && (mmsServer_isComponentAccess(alternateAccess) == false)) { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; continue; } - if (!mmsServer_isIndexAccess(alternateAccess)) { + if ((variable->type == MMS_ARRAY) && (mmsServer_isIndexAccess(alternateAccess) == false)) { accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; continue; } + + if (variable->type != MMS_ARRAY && variable->type != MMS_STRUCTURE) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; + continue; + } } Data_t* dataElement = writeRequest->listOfData.list.array[i]; @@ -661,72 +719,84 @@ mmsServer_handleWriteRequest( if (domain == NULL) domain = (MmsDomain*) device; - MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr); - - if (cachedArray == NULL) { - accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; - goto end_of_main_loop; - } + if (mmsServer_isIndexAccess(alternateAccess)) { + MmsValue* cachedArray = MmsServer_getValueFromCache(connection->server, domain, nameIdStr); - 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; goto end_of_main_loop; } - if (mmsServer_isAccessToArrayComponent(alternateAccess)) { - MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); + int index = mmsServer_getLowIndex(alternateAccess); + int numberOfElements = mmsServer_getNumberOfElements(alternateAccess); - if (namedVariable) { - elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue); - } + if (numberOfElements == 0) { /* select single array element with index */ - if ((namedVariable == NULL) || (elementValue == NULL)) { - accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + MmsValue* elementValue = MmsValue_getElement(cachedArray, index); + + if (elementValue == NULL) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_ATTRIBUTE_INCONSISTENT; goto end_of_main_loop; } - } - - if (MmsValue_update(elementValue, value) == false) { - accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; - goto end_of_main_loop; - } - } - else { /* select sub-array with start-index and number-of-elements */ - if (MmsValue_getType(value) != MMS_ARRAY) { - accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; - goto end_of_main_loop; - } + if (mmsServer_isAccessToArrayComponent(alternateAccess)) { + MmsVariableSpecification* namedVariable = MmsDomain_getNamedVariable(domain, nameIdStr); - int elementNo; + if (namedVariable) { + elementValue = mmsServer_getComponentOfArrayElement(alternateAccess, namedVariable, elementValue); + } - for (elementNo = 0; elementNo < numberOfElements; elementNo++) { - MmsValue* newElement = MmsValue_getElement(value, elementNo); - MmsValue* elementValue = MmsValue_getElement(cachedArray, index++); + if ((namedVariable == NULL) || (elementValue == NULL)) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + goto end_of_main_loop; + } + } - if ((elementValue == NULL) || (newElement == NULL) ) { + if (MmsValue_update(elementValue, value) == false) { accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } + } + else { /* select sub-array with start-index and number-of-elements */ - if (MmsValue_update(elementValue, newElement) == false) { + if (MmsValue_getType(value) != MMS_ARRAY) { accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; goto end_of_main_loop; } + int elementNo; + + for (elementNo = 0; elementNo < numberOfElements; elementNo++) { + MmsValue* newElement = MmsValue_getElement(value, elementNo); + MmsValue* elementValue = MmsValue_getElement(cachedArray, index++); + + if ((elementValue == NULL) || (newElement == NULL) ) { + accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + goto end_of_main_loop; + } + + if (MmsValue_update(elementValue, newElement) == false) { + accessResults[i] = DATA_ACCESS_ERROR_TYPE_INCONSISTENT; + goto end_of_main_loop; + } + } } - } - accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; - goto end_of_main_loop; + accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; + goto end_of_main_loop; + } + else if (mmsServer_isComponentAccess(alternateAccess)) { + variable = getComponent(connection, domain, alternateAccess, variable, nameIdStr); + if (variable == NULL) { + accessResults[i] = DATA_ACCESS_ERROR_OBJECT_NONE_EXISTENT; + goto end_of_main_loop; + } + } + else { + accessResults[i] = DATA_ACCESS_ERROR_SUCCESS; + goto end_of_main_loop; + } } /* Check for correct type */ @@ -756,7 +826,7 @@ mmsServer_handleWriteRequest( goto exit_function; } - exit_function: +exit_function: MmsServer_unlockModel(connection->server); From fd4fe9ad356d523324e2550300fa3e4a9b61a54c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Mon, 26 Jun 2023 20:45:13 +0100 Subject: [PATCH 053/214] - .NET API: Added finalizer to ControlObject (#459) --- dotnet/IEC61850forCSharp/Control.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dotnet/IEC61850forCSharp/Control.cs b/dotnet/IEC61850forCSharp/Control.cs index b3c2bcc9..97a3d767 100644 --- a/dotnet/IEC61850forCSharp/Control.cs +++ b/dotnet/IEC61850forCSharp/Control.cs @@ -775,7 +775,10 @@ namespace IEC61850 Dispose (true); } - + ~ControlObject() + { + Dispose (false); + } } } From eab2e6f2cbfb4bb6680d6f5961676465470b9587 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 29 Jun 2023 15:23:30 +0100 Subject: [PATCH 054/214] - fixed potential null pointer dereferentiation in IedConnection_getDeviceModelFromServer when no error argument given --- src/iec61850/client/ied_connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iec61850/client/ied_connection.c b/src/iec61850/client/ied_connection.c index bc581dea..bc8e9ff6 100644 --- a/src/iec61850/client/ied_connection.c +++ b/src/iec61850/client/ied_connection.c @@ -1757,7 +1757,7 @@ IedConnection_getDeviceModelFromServer(IedConnection self, IedClientError* error LinkedList_destroy(logicalDeviceNames); } else - *error = iedConnection_mapMmsErrorToIedError(mmsError); + if (error) *error = iedConnection_mapMmsErrorToIedError(mmsError); } LinkedList /**/ From c16314c426caf9da48d3f834d2c1bbbb2407177a Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 29 Jun 2023 16:54:54 +0100 Subject: [PATCH 055/214] - config file parser: added support for arrays of basic and complex data attributes including initialization (LIB61850-415) --- .../server/model/config_file_parser.c | 295 +++++++++++------- 1 file changed, 189 insertions(+), 106 deletions(-) diff --git a/src/iec61850/server/model/config_file_parser.c b/src/iec61850/server/model/config_file_parser.c index 8f2abeca..55a9f937 100644 --- a/src/iec61850/server/model/config_file_parser.c +++ b/src/iec61850/server/model/config_file_parser.c @@ -1,7 +1,7 @@ /* * config_file_parser.c * - * Copyright 2014-2022 Michael Zillgith + * Copyright 2014-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -28,6 +28,8 @@ #include "libiec61850_platform_includes.h" #include "stack_config.h" +#include + #define READ_BUFFER_MAX_SIZE 1024 static uint8_t lineBuffer[READ_BUFFER_MAX_SIZE]; @@ -114,6 +116,112 @@ ConfigFileParser_createModelFromConfigFileEx(const char* filename) return model; } +static bool +setValue(char* lineBuffer, DataAttribute* dataAttribute) +{ + char* valueIndicator = strchr((char*) lineBuffer, '='); + + if (valueIndicator != NULL) { + switch (dataAttribute->type) { + case IEC61850_UNICODE_STRING_255: + { + char* stringStart = valueIndicator + 2; + terminateString(stringStart, '"'); + dataAttribute->mmsValue = MmsValue_newMmsString(stringStart); + } + break; + + case IEC61850_VISIBLE_STRING_255: + case IEC61850_VISIBLE_STRING_129: + case IEC61850_VISIBLE_STRING_65: + case IEC61850_VISIBLE_STRING_64: + case IEC61850_VISIBLE_STRING_32: + case IEC61850_CURRENCY: + { + char* stringStart = valueIndicator + 2; + terminateString(stringStart, '"'); + dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart); + } + break; + + case IEC61850_INT8: + case IEC61850_INT16: + case IEC61850_INT32: + case IEC61850_INT64: + case IEC61850_INT128: + case IEC61850_ENUMERATED: + { + int32_t intValue; + if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue); + } + break; + + case IEC61850_INT8U: + case IEC61850_INT16U: + case IEC61850_INT24U: + case IEC61850_INT32U: + { + uint32_t uintValue; + if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue); + } + break; + + case IEC61850_FLOAT32: + { + float floatValue; + if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newFloat(floatValue); + } + break; + + case IEC61850_FLOAT64: + { + double doubleValue; + if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newDouble(doubleValue); + } + break; + + case IEC61850_BOOLEAN: + { + int boolean; + if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean); + } + break; + + case IEC61850_OPTFLDS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(-10); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + + case IEC61850_TRGOPS: + { + int value; + if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; + dataAttribute->mmsValue = MmsValue_newBitString(-6); + MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); + } + break; + + default: + break; + + } + } + + return true; + +exit_error: + return false; +} + IedModel* ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) { @@ -121,11 +229,14 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) bool stateInModel = false; int indendation = 0; + bool inArray = false; + bool inArrayElement = false; IedModel* model = NULL; LogicalDevice* currentLD = NULL; LogicalNode* currentLN = NULL; ModelNode* currentModelNode = NULL; + ModelNode* currentArrayNode = NULL; DataSet* currentDataSet = NULL; GSEControlBlock* currentGoCB = NULL; SVControlBlock* currentSMVCB = NULL; @@ -144,6 +255,18 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) if (bytesRead > 0) { lineBuffer[bytesRead] = 0; + /* trim trailing spaces */ + while (bytesRead > 1) { + bytesRead--; + + if (isspace(lineBuffer[bytesRead])) { + lineBuffer[bytesRead] = 0; + } + else { + break; + } + } + if (stateInModel) { if (StringUtils_startsWith((char*) lineBuffer, "}")) { @@ -161,11 +284,21 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) indendation = 3; } else if (indendation > 4) { + + if (inArrayElement && currentModelNode->parent == currentArrayNode) { + inArrayElement = false; + } + else { + indendation--; + } + + if (inArray && currentModelNode == currentArrayNode) { + inArray = false; + } + currentModelNode = currentModelNode->parent; - indendation--; } } - else if (indendation == 1) { if (StringUtils_startsWith((char*) lineBuffer, "LD")) { indendation = 2; @@ -210,7 +343,9 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) int arrayElements = 0; - sscanf((char*) lineBuffer, "DO(%129s %i)", nameString, &arrayElements); + if (sscanf((char*)lineBuffer, "DO(%129s %i)", nameString, &arrayElements) != 2) { + goto exit_error; + } currentModelNode = (ModelNode*) DataObject_create(nameString, (ModelNode*) currentLN, arrayElements); @@ -218,7 +353,10 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else if (StringUtils_startsWith((char*) lineBuffer, "DS")) { indendation = 4; - sscanf((char*) lineBuffer, "DS(%129s)", nameString); + if (sscanf((char*)lineBuffer, "DS(%129s)", nameString) != 1) { + goto exit_error; + } + terminateString(nameString, ')'); currentDataSet = DataSet_create(nameString, currentLN); @@ -296,7 +434,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) nameString3, confRef, fixedOffs, minTime, maxTime); indendation = 4; - } else if (StringUtils_startsWith((char*) lineBuffer, "SMVC")) { uint32_t confRev; @@ -313,7 +450,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) currentSMVCB = SVControlBlock_create(nameString, currentLN, nameString2, nameString3, confRev, smpMod, smpRate, optFlds, (bool) isUnicast); indendation = 4; - } #if (CONFIG_IEC61850_SETTING_GROUPS == 1) else if (StringUtils_startsWith((char*) lineBuffer, "SG")) { @@ -357,6 +493,41 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) currentModelNode = (ModelNode*) DataObject_create(nameString, currentModelNode, arrayElements); } + else if (StringUtils_startsWith((char*) lineBuffer, "[")) { + if (inArray == false) { + goto exit_error; + } + + int arrayIndex; + + if (sscanf((char*)lineBuffer, "[%i]", &arrayIndex) != 1) { + goto exit_error; + } + + if (StringUtils_endsWith((char*)lineBuffer, ";")) { + /* array of basic data attribute */ + ModelNode* arrayElementNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); + + if (arrayElementNode) { + setValue((char*)lineBuffer, (DataAttribute*)arrayElementNode); + } + else { + goto exit_error; + } + + } + else if (StringUtils_endsWith((char*)lineBuffer, "{")) { + /* array of constructed data attribtute */ + currentModelNode = ModelNode_getChildWithIdx(currentArrayNode, arrayIndex); + + if (currentModelNode) { + inArrayElement = true; + } + else { + goto exit_error; + } + } + } else if (StringUtils_startsWith((char*) lineBuffer, "DA")) { int arrayElements = 0; @@ -366,108 +537,20 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) int triggerOptions = 0; uint32_t sAddr = 0; - sscanf((char*) lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr); + if (sscanf((char*)lineBuffer, "DA(%129s %i %i %i %i %u)", nameString, &arrayElements, &attributeType, &functionalConstraint, &triggerOptions, &sAddr) != 6) { + goto exit_error; + } DataAttribute* dataAttribute = DataAttribute_create(nameString, currentModelNode, (DataAttributeType) attributeType, (FunctionalConstraint) functionalConstraint, triggerOptions, arrayElements, sAddr); - char* valueIndicator = strchr((char*) lineBuffer, '='); - - if (valueIndicator != NULL) { - switch (dataAttribute->type) { - case IEC61850_UNICODE_STRING_255: - { - char* stringStart = valueIndicator + 2; - terminateString(stringStart, '"'); - dataAttribute->mmsValue = MmsValue_newMmsString(stringStart); - } - break; - - case IEC61850_VISIBLE_STRING_255: - case IEC61850_VISIBLE_STRING_129: - case IEC61850_VISIBLE_STRING_65: - case IEC61850_VISIBLE_STRING_64: - case IEC61850_VISIBLE_STRING_32: - case IEC61850_CURRENCY: - { - char* stringStart = valueIndicator + 2; - terminateString(stringStart, '"'); - dataAttribute->mmsValue = MmsValue_newVisibleString(stringStart); - } - break; - - case IEC61850_INT8: - case IEC61850_INT16: - case IEC61850_INT32: - case IEC61850_INT64: - case IEC61850_INT128: - case IEC61850_ENUMERATED: - { - int32_t intValue; - if (sscanf(valueIndicator + 1, "%i", &intValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newIntegerFromInt32(intValue); - } - break; - - case IEC61850_INT8U: - case IEC61850_INT16U: - case IEC61850_INT24U: - case IEC61850_INT32U: - { - uint32_t uintValue; - if (sscanf(valueIndicator + 1, "%u", &uintValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newUnsignedFromUint32(uintValue); - } - break; - - case IEC61850_FLOAT32: - { - float floatValue; - if (sscanf(valueIndicator + 1, "%f", &floatValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newFloat(floatValue); - } - break; - - case IEC61850_FLOAT64: - { - double doubleValue; - if (sscanf(valueIndicator + 1, "%lf", &doubleValue) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newDouble(doubleValue); - } - break; - - case IEC61850_BOOLEAN: - { - int boolean; - if (sscanf(valueIndicator + 1, "%i", &boolean) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBoolean((bool) boolean); - } - break; - - case IEC61850_OPTFLDS: - { - int value; - if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBitString(-10); - MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); - } - break; - - case IEC61850_TRGOPS: - { - int value; - if (sscanf(valueIndicator + 1, "%i", &value) != 1) goto exit_error; - dataAttribute->mmsValue = MmsValue_newBitString(-6); - MmsValue_setBitStringFromIntegerBigEndian(dataAttribute->mmsValue, value); - } - break; - - default: - break; - - } + if (arrayElements > 0) { + inArray = true; + currentArrayNode = (ModelNode*)dataAttribute; } + setValue((char*)lineBuffer, dataAttribute); + int lineLength = (int) strlen((char*) lineBuffer); if (lineBuffer[lineLength - 1] == '{') { @@ -541,8 +624,6 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) else goto exit_error; } - - } else { if (StringUtils_startsWith((char*) lineBuffer, "MODEL{")) { @@ -552,7 +633,9 @@ ConfigFileParser_createModelFromConfigFile(FileHandle fileHandle) indendation = 1; } else if (StringUtils_startsWith((char*) lineBuffer, "MODEL(")) { - sscanf((char*) lineBuffer, "MODEL(%129s)", nameString); + if (sscanf((char*)lineBuffer, "MODEL(%129s)", nameString) != 1) + goto exit_error; + terminateString(nameString, ')'); model = IedModel_create(nameString); stateInModel = true; From 665501c9faf57474a4d57ac2447e698ac2f4545b Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 13 Jul 2023 19:36:43 +0100 Subject: [PATCH 056/214] - extended data model helper function to be able to be used in array elements --- src/iec61850/server/model/cdc.c | 87 ++++++++++++++++----------------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/src/iec61850/server/model/cdc.c b/src/iec61850/server/model/cdc.c index 122fa8bd..211e925e 100644 --- a/src/iec61850/server/model/cdc.c +++ b/src/iec61850/server/model/cdc.c @@ -3,7 +3,7 @@ * * Helper functions for the dynamic creation of Common Data Classes (CDCs) * - * Copyright 2014 Michael Zillgith + * Copyright 2014-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -35,7 +35,7 @@ DataAttribute* CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool isIntegerNotFloat) { - DataAttribute* analogeValue = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* analogeValue = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); if (isIntegerNotFloat) DataAttribute_create("i", (ModelNode*) analogeValue, IEC61850_INT32, fc, triggerOptions, 0, 0); @@ -48,7 +48,7 @@ CAC_AnalogueValue_create(const char* name, ModelNode* parent, FunctionalConstrai DataAttribute* CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasTransientIndicator) { - DataAttribute* valWithTrans = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* valWithTrans = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); DataAttribute_create("posVal", (ModelNode*) valWithTrans, IEC61850_INT8, fc, triggerOptions, 0, 0); @@ -64,7 +64,7 @@ CAC_ValWithTrans_create(const char* name, ModelNode* parent, FunctionalConstrain DataAttribute* CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, FunctionalConstraint fc, uint8_t triggerOptions) { - DataAttribute* vector = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* vector = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); CAC_AnalogueValue_create("mag", (ModelNode*) vector, fc, triggerOptions, false); @@ -77,7 +77,7 @@ CAC_Vector_create(const char* name, ModelNode* parent, uint32_t options, Functio DataAttribute* CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, uint8_t triggerOptions, bool hasZVal) { - DataAttribute* point = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); + DataAttribute* point = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, fc, triggerOptions, 0, 0); DataAttribute_create("xVal", (ModelNode*) point, IEC61850_FLOAT32, fc, triggerOptions, 0, 0); DataAttribute_create("yVal", (ModelNode*) point, IEC61850_FLOAT32, fc, triggerOptions, 0, 0); @@ -91,7 +91,7 @@ CAC_Point_create(const char* name, ModelNode* parent, FunctionalConstraint fc, u DataAttribute* CAC_ScaledValueConfig_create(const char* name, ModelNode* parent) { - DataAttribute* scaling = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + DataAttribute* scaling = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("scaleFactor", (ModelNode*) scaling, IEC61850_FLOAT32, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("offset", (ModelNode*) scaling, IEC61850_FLOAT32, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); @@ -102,7 +102,7 @@ CAC_ScaledValueConfig_create(const char* name, ModelNode* parent) DataAttribute* CAC_Unit_create(const char* name, ModelNode* parent, bool hasMagnitude) { - DataAttribute* unit = DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); + DataAttribute* unit = (name == NULL) ? (DataAttribute*)parent : DataAttribute_create(name, parent, IEC61850_CONSTRUCTED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("SIUnit", (ModelNode*) unit, IEC61850_ENUMERATED, IEC61850_FC_CF, TRG_OPT_DATA_CHANGED, 0, 0); @@ -255,7 +255,7 @@ CDC_addStandardOptions(DataObject* dataObject, uint32_t options) DataObject* CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newSPS, IEC61850_BOOLEAN); @@ -273,7 +273,7 @@ CDC_SPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newDPS, IEC61850_CODEDENUM); @@ -291,7 +291,7 @@ CDC_DPS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newINS = DataObject_create(dataObjectName, parent, 0); + DataObject* newINS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newINS, IEC61850_INT32); @@ -310,7 +310,7 @@ CDC_INS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newENS = DataObject_create(dataObjectName, parent, 0); + DataObject* newENS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newENS, IEC61850_ENUMERATED); @@ -328,7 +328,7 @@ CDC_ENS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newBCR = DataObject_create(dataObjectName, parent, 0); + DataObject* newBCR = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("actVal", (ModelNode*) newBCR, IEC61850_INT64, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -359,7 +359,7 @@ CDC_BCR_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSEC = DataObject_create(dataObjectName, parent, 0); + DataObject* newSEC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("cnt", (ModelNode*) newSEC, IEC61850_INT32U, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("sev", (ModelNode*) newSEC, IEC61850_ENUMERATED, IEC61850_FC_ST, 0, 0, 0); @@ -380,7 +380,7 @@ CDC_SEC_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPS = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_addStatusToDataObject(newSPS, IEC61850_VISIBLE_STRING_255); @@ -403,7 +403,7 @@ CDC_VSS_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newMV = DataObject_create(dataObjectName, parent, 0); + DataObject* newMV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (options & CDC_OPTION_INST_MAG) CAC_AnalogueValue_create("instMag", (ModelNode*) newMV, IEC61850_FC_MX, 0, isIntegerNotFloat); @@ -432,7 +432,7 @@ CDC_MV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, b DataObject* CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newMV = DataObject_create(dataObjectName, parent, 0); + DataObject* newMV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (options & CDC_OPTION_INST_MAG) CAC_Vector_create("instCVal", (ModelNode*) newMV, options, IEC61850_FC_MX, 0); @@ -465,7 +465,7 @@ CDC_CMV_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newSAV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSAV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CAC_AnalogueValue_create("instMag", (ModelNode*) newSAV, IEC61850_FC_MX, 0, isIntegerNotFloat); @@ -491,7 +491,7 @@ CDC_SAV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_HST_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint16_t maxPts) { - DataObject* newHST = DataObject_create(dataObjectName, parent, 0); + DataObject* newHST = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("hstVal", (ModelNode*) newHST, IEC61850_INT32, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED | TRG_OPT_DATA_UPDATE, maxPts, 0); @@ -577,7 +577,7 @@ addCommonControlAttributes(DataObject* dobj, uint32_t controlOptions) DataObject* CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newSPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newSPC, controlOptions); @@ -613,7 +613,7 @@ CDC_SPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_DPC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newDPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newDPC, controlOptions); @@ -695,7 +695,7 @@ addControlStatusAttributesForAnalogControl(DataObject* dobj, uint32_t controlOpt DataObject* CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { - DataObject* newAPC = DataObject_create(dataObjectName, parent, 0); + DataObject* newAPC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addControlStatusAttributesForAnalogControl(newAPC, controlOptions); @@ -728,7 +728,7 @@ CDC_APC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newINC = DataObject_create(dataObjectName, parent, 0); + DataObject* newINC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newINC, controlOptions); @@ -764,7 +764,7 @@ CDC_INC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions) { - DataObject* newENC = DataObject_create(dataObjectName, parent, 0); + DataObject* newENC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newENC, controlOptions); @@ -791,7 +791,7 @@ CDC_ENC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) { - DataObject* newBSC = DataObject_create(dataObjectName, parent, 0); + DataObject* newBSC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newBSC, controlOptions); @@ -821,7 +821,7 @@ CDC_BSC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool hasTransientIndicator) { - DataObject* newISC = DataObject_create(dataObjectName, parent, 0); + DataObject* newISC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addOriginatorAndCtlNumOptions((ModelNode*) newISC, controlOptions); @@ -855,7 +855,7 @@ CDC_ISC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, bool isIntegerNotFloat) { - DataObject* newBAC = DataObject_create(dataObjectName, parent, 0); + DataObject* newBAC = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); addControlStatusAttributesForAnalogControl(newBAC, controlOptions); @@ -899,7 +899,7 @@ CDC_BAC_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newLPL = DataObject_create(dataObjectName, parent, 0); + DataObject* newLPL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("vendor", (ModelNode*) newLPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); DataAttribute_create("swRev", (ModelNode*) newLPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); @@ -921,7 +921,7 @@ CDC_LPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDPL = DataObject_create(dataObjectName, parent, 0); + DataObject* newDPL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("vendor", (ModelNode*) newDPL, IEC61850_VISIBLE_STRING_255, IEC61850_FC_DC, 0, 0, 0); @@ -949,7 +949,7 @@ CDC_DPL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newACD = DataObject_create(dataObjectName, parent, 0); + DataObject* newACD = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("general", (ModelNode*) newACD, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); DataAttribute_create("dirGeneral", (ModelNode*) newACD, IEC61850_ENUMERATED, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -984,7 +984,7 @@ CDC_ACD_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newACT = DataObject_create(dataObjectName, parent, 0); + DataObject* newACT = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("general", (ModelNode*) newACT, IEC61850_BOOLEAN, IEC61850_FC_ST, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1010,7 +1010,7 @@ CDC_ACT_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newWYE = DataObject_create(dataObjectName, parent, 0); + DataObject* newWYE = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); /* TODO check if some options should be masked */ /* TODO take care for GC_1 */ @@ -1033,7 +1033,7 @@ CDC_WYE_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newDEL = DataObject_create(dataObjectName, parent, 0); + DataObject* newDEL = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); /* TODO check if some options should be masked */ CDC_CMV_create("phsAB", (ModelNode*) newDEL, options); @@ -1052,7 +1052,7 @@ CDC_DEL_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPG = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_BOOLEAN, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1064,7 +1064,7 @@ CDC_SPG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newSPG = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newSPG, IEC61850_VISIBLE_STRING_255, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1077,7 +1077,7 @@ CDC_VSG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newENG = DataObject_create(dataObjectName, parent, 0); + DataObject* newENG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newENG, IEC61850_ENUMERATED, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1089,7 +1089,7 @@ CDC_ENG_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options) { - DataObject* newING = DataObject_create(dataObjectName, parent, 0); + DataObject* newING = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); DataAttribute_create("setVal", (ModelNode*) newING, IEC61850_INT32, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, 0, 0); @@ -1114,7 +1114,7 @@ CDC_ING_create(const char* dataObjectName, ModelNode* parent, uint32_t options) DataObject* CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, bool isIntegerNotFloat) { - DataObject* newASG = DataObject_create(dataObjectName, parent, 0); + DataObject* newASG = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CAC_AnalogueValue_create("setMag", (ModelNode*) newASG, IEC61850_FC_SP, TRG_OPT_DATA_CHANGED, isIntegerNotFloat); @@ -1145,7 +1145,7 @@ CDC_ASG_create(const char* dataObjectName, ModelNode* parent, uint32_t options, DataObject* CDC_SPV_create(const char* dataObjectName, ModelNode* parent, uint32_t options, uint32_t controlOptions, uint32_t wpOptions, bool hasChaManRs) { - DataObject* newSPV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSPV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); if (hasChaManRs) CDC_SPC_create("chaManRs", (ModelNode*) newSPV, 0, CDC_CTL_MODEL_DIRECT_NORMAL); @@ -1196,7 +1196,7 @@ CDC_STV_create(const char* dataObjectName, ModelNode* parent, (void)controlOptions; /* TODO implement */ (void)wpOptions; /* TODO implement */ - DataObject* newSTV = DataObject_create(dataObjectName, parent, 0); + DataObject* newSTV = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_INS_create("actSt", (ModelNode*) newSTV, 0); @@ -1218,7 +1218,7 @@ CDC_ALM_create(const char* dataObjectName, ModelNode* parent, (void)controlOptions; /* TODO implement */ (void)wpOptions; /* TODO implement */ - DataObject* newALM = DataObject_create(dataObjectName, parent, 0); + DataObject* newALM = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("almAck", (ModelNode*) newALM, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1244,7 +1244,7 @@ CDC_CMD_create(const char* dataObjectName, ModelNode* parent, (void)hasCmTm; /* TODO implement */ (void)hasCmCt; /* TODO implement */ - DataObject* newCMD = DataObject_create(dataObjectName, parent, 0); + DataObject* newCMD = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_INC_create("actSt", (ModelNode*) newCMD, 0, controlOptions); @@ -1272,7 +1272,7 @@ CDC_CTE_create(const char* dataObjectName, ModelNode* parent, { (void)controlOptions; /* TODO implement */ - DataObject* newCTE = DataObject_create(dataObjectName, parent, 0); + DataObject* newCTE = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("manRs", (ModelNode*) newCTE, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1313,7 +1313,7 @@ CDC_TMS_create(const char* dataObjectName, ModelNode* parent, { (void)controlOptions; /* TODO implement */ - DataObject* newTMS = DataObject_create(dataObjectName, parent, 0); + DataObject* newTMS = (dataObjectName == NULL) ? (DataObject*)parent : DataObject_create(dataObjectName, parent, 0); CDC_SPC_create("manRs", (ModelNode*) newTMS, 0, CDC_CTL_MODEL_DIRECT_NORMAL | CDC_CTL_OPTION_ORIGIN); @@ -1344,4 +1344,3 @@ CDC_TMS_create(const char* dataObjectName, ModelNode* parent, return newTMS; } - From 3c918ee4e31eddf1ff2308363c700fccc6a4d247 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 14:46:53 +0100 Subject: [PATCH 057/214] - IED server: added IedServer_ListObjectsAccessHandler callback to control LISTOBJECTS access rights (LIB61850-417) --- src/iec61850/inc/iec61850_server.h | 20 +++ .../inc_private/mms_mapping_internal.h | 3 + src/iec61850/server/impl/ied_server.c | 7 + src/iec61850/server/mms_mapping/mms_mapping.c | 137 +++++++++++++++++- src/mms/inc_private/mms_server_internal.h | 6 + src/mms/inc_private/mms_server_libinternal.h | 6 + .../iso_mms/server/mms_get_namelist_service.c | 36 +++-- src/mms/iso_mms/server/mms_server.c | 35 +++++ 8 files changed, 233 insertions(+), 17 deletions(-) diff --git a/src/iec61850/inc/iec61850_server.h b/src/iec61850/inc/iec61850_server.h index 3db3235e..94324a00 100644 --- a/src/iec61850/inc/iec61850_server.h +++ b/src/iec61850/inc/iec61850_server.h @@ -2007,6 +2007,26 @@ typedef bool LIB61850_API void IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHandler handler, void* parameter); +/** + * \brief Callback that is called when a client is invoking a list objects service + * + * \param parameter user provided parameter + * \param connection client connection that is involved + * + * \return true to include the object in the service response, otherwise false + */ +typedef bool +(*IedServer_ListObjectsAccessHandler) (void* parameter, ClientConnection connection, LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc); + +/** + * \brief Set a handler to control which objects are return by the list objects services + * + * \param handler the callback handler to be used + * \param parameter a user provided parameter that is passed to the handler. + */ +LIB61850_API void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter); + /**@}*/ /**@}*/ diff --git a/src/iec61850/inc_private/mms_mapping_internal.h b/src/iec61850/inc_private/mms_mapping_internal.h index 51714f11..6e3edb52 100644 --- a/src/iec61850/inc_private/mms_mapping_internal.h +++ b/src/iec61850/inc_private/mms_mapping_internal.h @@ -352,6 +352,9 @@ struct sMmsMapping { IedServer_DirectoryAccessHandler directoryAccessHandler; void* directoryAccessHandlerParameter; + + IedServer_ListObjectsAccessHandler listObjectsAccessHandler; + void* listObjectsAccessHandlerParameter; }; #endif /* MMS_MAPPING_INTERNAL_H_ */ diff --git a/src/iec61850/server/impl/ied_server.c b/src/iec61850/server/impl/ied_server.c index 27d40624..aa8e84cd 100644 --- a/src/iec61850/server/impl/ied_server.c +++ b/src/iec61850/server/impl/ied_server.c @@ -1967,3 +1967,10 @@ IedServer_setDirectoryAccessHandler(IedServer self, IedServer_DirectoryAccessHan self->mmsMapping->directoryAccessHandler = handler; self->mmsMapping->directoryAccessHandlerParameter = parameter; } + +void +IedServer_setListObjectsAccessHandler(IedServer self, IedServer_ListObjectsAccessHandler handler, void* parameter) +{ + self->mmsMapping->listObjectsAccessHandler = handler; + self->mmsMapping->listObjectsAccessHandlerParameter = parameter; +} diff --git a/src/iec61850/server/mms_mapping/mms_mapping.c b/src/iec61850/server/mms_mapping/mms_mapping.c index 2d76f39c..91e12f62 100644 --- a/src/iec61850/server/mms_mapping/mms_mapping.c +++ b/src/iec61850/server/mms_mapping/mms_mapping.c @@ -3314,6 +3314,122 @@ mmsConnectionHandler(void* parameter, MmsServerConnection connection, MmsServerE } } +static bool +mmsListObjectsAccessHandler(void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection) +{ + MmsMapping* self = (MmsMapping*) parameter; + + if (DEBUG_IED_SERVER) + printf("IED_SERVER: mmsListObjectsAccessHandler: Requested %s\n", variableId); + + bool allowAccess = true; + + if (self->listObjectsAccessHandler) + { + char* separator = strchr(variableId, '$'); + +#if (CONFIG_IEC61850_SETTING_GROUPS == 1) + + if (separator) { + if (isFunctionalConstraint("SE", separator)) { + goto exit_function; + } + } + +#endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ + + char* ldName = MmsDomain_getName(domain); + + LogicalDevice* ld = IedModel_getDevice(self->model, ldName); + + if (ld) + { + FunctionalConstraint fc = IEC61850_FC_NONE; + + if (separator) { + fc = FunctionalConstraint_fromString(separator + 1); + + if (fc == IEC61850_FC_BR || fc == IEC61850_FC_US || + fc == IEC61850_FC_MS || fc == IEC61850_FC_RP || + fc == IEC61850_FC_LG || fc == IEC61850_FC_GO) + { + goto exit_function; + } + else + { + char str[65]; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); + + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); + + if (ln != NULL) + { + char* doStart = strchr(separator + 1, '$'); + + if (doStart != NULL) { + + char* doEnd = strchr(doStart + 1, '$'); + + if (doEnd == NULL) { + StringUtils_copyStringToBuffer(doStart + 1, str); + } + else { + doEnd--; + + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) (doStart + 1), doEnd - doStart); + } + + if (fc == IEC61850_FC_SP) { + if (!strcmp(str, "SGCB")) + goto exit_function; + } + + ModelNode* dobj = ModelNode_getChild((ModelNode*) ln, str); + + if (dobj != NULL) { + + if (dobj->modelType == DataObjectModelType) { + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, (DataObject*) dobj, fc); + } + } + } + else { + /* no data object but with FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, + connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, NULL, fc); + } + } + } + } + else { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln) { + /* only LN, no FC specified */ + + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + allowAccess = self->listObjectsAccessHandler(self->listObjectsAccessHandlerParameter, clientConnection, ld, ln, NULL, fc); + } + } + } + else { + /* internal error ? - we should not end up here! */ + } + } + +exit_function: + return allowAccess; +} + static MmsDataAccessError mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsServerConnection connection, bool isDirectAccess) { @@ -3350,11 +3466,9 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS LogicalDevice* ld = IedModel_getDevice(self->model, ldName); - if (ld != NULL) { - - char str[65]; - - FunctionalConstraint fc; + if (ld != NULL) + { + FunctionalConstraint fc = IEC61850_FC_NONE; if (separator != NULL) { fc = FunctionalConstraint_fromString(separator + 1); @@ -3367,6 +3481,8 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } else { + char str[65]; + StringUtils_createStringFromBufferInBuffer(str, (uint8_t*) variableId, separator - variableId); LogicalNode* ln = LogicalDevice_getLogicalNode(ld, str); @@ -3417,6 +3533,16 @@ mmsReadAccessHandler (void* parameter, MmsDomain* domain, char* variableId, MmsS } } } + else { + LogicalNode* ln = LogicalDevice_getLogicalNode(ld, variableId); + + if (ln != NULL) { + ClientConnection clientConnection = private_IedServer_getClientConnectionByHandle(self->iedServer, connection); + + return self->readAccessHandler(ld, ln, NULL, fc, clientConnection, + self->readAccessHandlerParameter); + } + } } return DATA_ACCESS_ERROR_OBJECT_ACCESS_UNSUPPORTED; @@ -3666,6 +3792,7 @@ MmsMapping_installHandlers(MmsMapping* self) MmsServer_installReadHandler(self->mmsServer, mmsReadHandler, (void*) self); MmsServer_installWriteHandler(self->mmsServer, mmsWriteHandler, (void*) self); MmsServer_installReadAccessHandler(self->mmsServer, mmsReadAccessHandler, (void*) self); + MmsServer_installListAccessHandler(self->mmsServer, mmsListObjectsAccessHandler, (void*) self); MmsServer_installConnectionHandler(self->mmsServer, mmsConnectionHandler, (void*) self); MmsServer_installVariableListAccessHandler(self->mmsServer, variableListAccessHandler, (void*) self); MmsServer_installGetNameListHandler(self->mmsServer, mmsGetNameListHandler, (void*) self); diff --git a/src/mms/inc_private/mms_server_internal.h b/src/mms/inc_private/mms_server_internal.h index ec70d359..606e0827 100644 --- a/src/mms/inc_private/mms_server_internal.h +++ b/src/mms/inc_private/mms_server_internal.h @@ -120,6 +120,9 @@ struct sMmsServer { MmsWriteVariableHandler writeHandler; void* writeHandlerParameter; + MmsListAccessHandler listAccessHandler; + void* listAccessHandlerParameter; + MmsConnectionHandler connectionHandler; void* connectionHandlerParameter; @@ -422,6 +425,9 @@ mmsServer_setValue(MmsServer self, MmsDomain* domain, char* itemId, MmsValue* va LIB61850_INTERNAL MmsValue* mmsServer_getValue(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess); +LIB61850_INTERNAL bool +mmsServer_checkListAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection); + LIB61850_INTERNAL void mmsServer_createMmsWriteResponse(MmsServerConnection connection, uint32_t invokeId, ByteBuffer* response, int numberOfItems, MmsDataAccessError* accessResults); diff --git a/src/mms/inc_private/mms_server_libinternal.h b/src/mms/inc_private/mms_server_libinternal.h index 444989c9..da5de7a5 100644 --- a/src/mms/inc_private/mms_server_libinternal.h +++ b/src/mms/inc_private/mms_server_libinternal.h @@ -37,6 +37,9 @@ typedef MmsDataAccessError (*MmsWriteVariableHandler)(void* parameter, MmsDomain* domain, char* variableId, MmsValue* value, MmsServerConnection connection); +typedef bool (*MmsListAccessHandler) (void* parameter, MmsDomain* domain, + char* variableId, MmsServerConnection connection); + typedef void (*MmsConnectionHandler)(void* parameter, MmsServerConnection connection, MmsServerEvent event); @@ -63,6 +66,9 @@ LIB61850_INTERNAL void MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler, void* parameter); +LIB61850_INTERNAL void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter); + /** * A connection handler will be invoked whenever a new client connection is opened or closed */ diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 2170f8c1..0a9c6f57 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -129,7 +129,7 @@ appendMmsSubVariable(char* name, char* child) } static LinkedList -addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpecification* variable) +addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameList, MmsDomain* domain, char* prefix, MmsVariableSpecification* variable) { LinkedList listElement = nameList; @@ -158,14 +158,20 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ if (variableName) - { - listElement = LinkedList_insertAfter(listElement, variableName); + { + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, variableName, connection); + + if (accessAllowed) { + + listElement = LinkedList_insertAfter(listElement, variableName); #if (CONFIG_MMS_SORT_NAME_LIST == 1) - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[index[i]]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[index[i]]); #else - listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[i]); + listElement = addSubNamedVaribleNamesToList(connection, listElement, domain, variableName, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ + + } } } @@ -233,7 +239,7 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) bool allowAccess = true; if (connection->server->getNameListHandler) { - allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATASETS, domain, connection); + allowAccess = connection->server->getNameListHandler(connection->server->getNameListHandlerParameter, MMS_GETNAMELIST_DATA, domain, connection); } if (allowAccess) { @@ -255,22 +261,28 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) for (i = 0; i < domain->namedVariablesCount; i++) { + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, variables[index[i]]->name, connection); + + if (accessAllowed) { + #if (CONFIG_MMS_SORT_NAME_LIST == 1) - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[index[i]]->name)); #else - element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); + element = LinkedList_insertAfter(element, StringUtils_copyString(variables[i]->name)); #endif #if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) #if (CONFIG_MMS_SORT_NAME_LIST == 1) - char* prefix = variables[index[i]]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[index[i]]); + char* prefix = variables[index[i]]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[index[i]]); #else - char* prefix = variables[i]->name; - element = addSubNamedVaribleNamesToList(element, prefix, variables[i]); + char* prefix = variables[i]->name; + element = addSubNamedVaribleNamesToList(connection, element, domain, prefix, variables[i]); #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ + } + } #if (CONFIG_MMS_SORT_NAME_LIST == 1) diff --git a/src/mms/iso_mms/server/mms_server.c b/src/mms/iso_mms/server/mms_server.c index b9d4c798..3229b21e 100644 --- a/src/mms/iso_mms/server/mms_server.c +++ b/src/mms/iso_mms/server/mms_server.c @@ -351,6 +351,13 @@ MmsServer_installWriteHandler(MmsServer self, MmsWriteVariableHandler writeHandl self->writeHandlerParameter = parameter; } +void +MmsServer_installListAccessHandler(MmsServer self, MmsListAccessHandler listAccessHandler, void* parameter) +{ + self->listAccessHandler = listAccessHandler; + self->listAccessHandlerParameter = parameter; +} + void MmsServer_installConnectionHandler(MmsServer self, MmsConnectionHandler connectionHandler, void* parameter) { @@ -579,6 +586,34 @@ exit_function: return value; } +MmsDataAccessError +mmsServer_checkReadAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection, bool isDirectAccess) +{ + MmsDataAccessError accessError = DATA_ACCESS_ERROR_SUCCESS; + + printf("mmsServer_checkReadAccess(%s/%s)\n", domain->domainName, itemId); + + if (self->readAccessHandler) { + accessError = + self->readAccessHandler(self->readAccessHandlerParameter, (domain == (MmsDomain*) self->device) ? NULL : domain, + itemId, connection, isDirectAccess); + } + + return accessError; +} + +bool +mmsServer_checkListAccess(MmsServer self, MmsDomain* domain, char* itemId, MmsServerConnection connection) +{ + bool allowAccess = true; + + if (self->listAccessHandler) { + allowAccess = self->listAccessHandler(self->listAccessHandlerParameter, domain, itemId, connection); + } + + return allowAccess; +} + MmsDevice* MmsServer_getDevice(MmsServer self) { From 2467605e2358a17d9fc8ba0d24d3303fb8704796 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 15:28:00 +0100 Subject: [PATCH 058/214] - IED server: apply LISTOBJECTS restrictions to get-variable-access-attributes servic (LIB61850-417) --- .../iso_mms/server/mms_get_namelist_service.c | 3 +++ .../server/mms_get_var_access_service.c | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 0a9c6f57..6d6c1568 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -172,6 +172,9 @@ addSubNamedVaribleNamesToList(MmsServerConnection connection, LinkedList nameLis #endif /* (CONFIG_MMS_SORT_NAME_LIST == 1) */ } + else { + GLOBAL_FREEMEM(variableName); + } } } diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index abdf6d02..d936701c 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -1,7 +1,7 @@ /* * mms_get_var_access_service.c * - * Copyright 2013 Michael Zillgith + * Copyright 2013-2023 Michael Zillgith * * This file is part of libIEC61850. * @@ -191,7 +191,8 @@ deleteVariableAccessAttributesResponse( getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; - } else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { + } + else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; @@ -215,8 +216,10 @@ createVariableAccessAttributesResponse( MmsVariableSpecification* namedVariable = NULL; + MmsDomain* domain = NULL; + if (domainId != NULL) { - MmsDomain* domain = MmsDevice_getDomain(device, domainId); + domain = MmsDevice_getDomain(device, domainId); if (domain == NULL) { if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); @@ -233,9 +236,20 @@ createVariableAccessAttributesResponse( namedVariable = MmsDevice_getNamedVariable(device, nameId); #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - if (namedVariable == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s not known\n", nameId); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); + + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + + goto exit_function; + } + + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); + + if (!accessAllowed) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); From efec5194a284069921fb37920a3ce033a9285454 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 15:37:34 +0100 Subject: [PATCH 059/214] - replaced tabs by spaces in mms_get_var_access_service.c --- .../server/mms_get_var_access_service.c | 513 +++++++++--------- 1 file changed, 256 insertions(+), 257 deletions(-) diff --git a/src/mms/iso_mms/server/mms_get_var_access_service.c b/src/mms/iso_mms/server/mms_get_var_access_service.c index d936701c..840d90b5 100644 --- a/src/mms/iso_mms/server/mms_get_var_access_service.c +++ b/src/mms/iso_mms/server/mms_get_var_access_service.c @@ -32,250 +32,250 @@ static int createTypeSpecification ( - MmsVariableSpecification* namedVariable, - TypeSpecification_t* typeSpec) + MmsVariableSpecification* namedVariable, + TypeSpecification_t* typeSpec) { - if (namedVariable->type == MMS_ARRAY) { - typeSpec->present = TypeSpecification_PR_array; - - asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), - (long) namedVariable->typeSpec.array.elementCount); - - typeSpec->choice.array.packed = NULL; - typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, - typeSpec->choice.array.elementType); - } - else if (namedVariable->type == MMS_STRUCTURE) { - - typeSpec->present = TypeSpecification_PR_structure; - - int componentCount = namedVariable->typeSpec.structure.elementCount; - - typeSpec->choice.structure.components.list.count = componentCount; - typeSpec->choice.structure.components.list.size = componentCount; - - typeSpec->choice.structure.components.list.array - = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); - - int i; - - for (i = 0; i < componentCount; i++) { - - typeSpec->choice.structure.components.list.array[i] = - (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName = - (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); - - typeSpec->choice.structure.components.list.array[i]->componentName->buf = - (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentName->size = - strlen(namedVariable->typeSpec.structure.elements[i]->name); - - typeSpec->choice.structure.components.list.array[i]->componentType = - (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); - - createTypeSpecification(namedVariable->typeSpec.structure.elements[i], - typeSpec->choice.structure.components.list.array[i]->componentType); - } - } - else { - - switch (namedVariable->type) { - case MMS_BOOLEAN: - typeSpec->present = TypeSpecification_PR_boolean; - break; - case MMS_BIT_STRING: - typeSpec->present = TypeSpecification_PR_bitstring; - typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; - break; - case MMS_INTEGER: - typeSpec->present = TypeSpecification_PR_integer; - typeSpec->choice.integer = namedVariable->typeSpec.integer; - break; - case MMS_UNSIGNED: - typeSpec->present = TypeSpecification_PR_unsigned; - typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; - break; - case MMS_FLOAT: - typeSpec->present = TypeSpecification_PR_floatingpoint; - typeSpec->choice.floatingpoint.exponentwidth = - namedVariable->typeSpec.floatingpoint.exponentWidth; - typeSpec->choice.floatingpoint.formatwidth = - namedVariable->typeSpec.floatingpoint.formatWidth; - break; - case MMS_OCTET_STRING: - typeSpec->present = TypeSpecification_PR_octetstring; - typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; - break; - case MMS_VISIBLE_STRING: - typeSpec->present = TypeSpecification_PR_visiblestring; - typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; - break; - case MMS_STRING: - typeSpec->present = TypeSpecification_PR_mMSString; - typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; - break; - case MMS_UTC_TIME: - typeSpec->present = TypeSpecification_PR_utctime; - break; - case MMS_BINARY_TIME: - typeSpec->present = TypeSpecification_PR_binarytime; - - if (namedVariable->typeSpec.binaryTime == 6) - typeSpec->choice.binarytime = 1; - else - typeSpec->choice.binarytime = 0; - - break; - default: - if (DEBUG_MMS_SERVER) - printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); - return -1; - break; - } - } - - return 1; + if (namedVariable->type == MMS_ARRAY) { + typeSpec->present = TypeSpecification_PR_array; + + asn_long2INTEGER(&(typeSpec->choice.array.numberOfElements), + (long) namedVariable->typeSpec.array.elementCount); + + typeSpec->choice.array.packed = NULL; + typeSpec->choice.array.elementType = (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.array.elementTypeSpec, + typeSpec->choice.array.elementType); + } + else if (namedVariable->type == MMS_STRUCTURE) { + + typeSpec->present = TypeSpecification_PR_structure; + + int componentCount = namedVariable->typeSpec.structure.elementCount; + + typeSpec->choice.structure.components.list.count = componentCount; + typeSpec->choice.structure.components.list.size = componentCount; + + typeSpec->choice.structure.components.list.array + = (StructComponent_t**) GLOBAL_CALLOC(componentCount, sizeof(StructComponent_t*)); + + int i; + + for (i = 0; i < componentCount; i++) { + + typeSpec->choice.structure.components.list.array[i] = + (StructComponent_t*) GLOBAL_CALLOC(1, sizeof(StructComponent_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName = + (Identifier_t*) GLOBAL_CALLOC(1, sizeof(Identifier_t)); + + typeSpec->choice.structure.components.list.array[i]->componentName->buf = + (uint8_t*) StringUtils_copyString(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentName->size = + strlen(namedVariable->typeSpec.structure.elements[i]->name); + + typeSpec->choice.structure.components.list.array[i]->componentType = + (TypeSpecification_t*) GLOBAL_CALLOC(1, sizeof(TypeSpecification_t)); + + createTypeSpecification(namedVariable->typeSpec.structure.elements[i], + typeSpec->choice.structure.components.list.array[i]->componentType); + } + } + else { + + switch (namedVariable->type) { + case MMS_BOOLEAN: + typeSpec->present = TypeSpecification_PR_boolean; + break; + case MMS_BIT_STRING: + typeSpec->present = TypeSpecification_PR_bitstring; + typeSpec->choice.bitstring = namedVariable->typeSpec.bitString; + break; + case MMS_INTEGER: + typeSpec->present = TypeSpecification_PR_integer; + typeSpec->choice.integer = namedVariable->typeSpec.integer; + break; + case MMS_UNSIGNED: + typeSpec->present = TypeSpecification_PR_unsigned; + typeSpec->choice.Unsigned = namedVariable->typeSpec.unsignedInteger; + break; + case MMS_FLOAT: + typeSpec->present = TypeSpecification_PR_floatingpoint; + typeSpec->choice.floatingpoint.exponentwidth = + namedVariable->typeSpec.floatingpoint.exponentWidth; + typeSpec->choice.floatingpoint.formatwidth = + namedVariable->typeSpec.floatingpoint.formatWidth; + break; + case MMS_OCTET_STRING: + typeSpec->present = TypeSpecification_PR_octetstring; + typeSpec->choice.octetstring = namedVariable->typeSpec.octetString; + break; + case MMS_VISIBLE_STRING: + typeSpec->present = TypeSpecification_PR_visiblestring; + typeSpec->choice.visiblestring = namedVariable->typeSpec.visibleString; + break; + case MMS_STRING: + typeSpec->present = TypeSpecification_PR_mMSString; + typeSpec->choice.mMSString = namedVariable->typeSpec.mmsString; + break; + case MMS_UTC_TIME: + typeSpec->present = TypeSpecification_PR_utctime; + break; + case MMS_BINARY_TIME: + typeSpec->present = TypeSpecification_PR_binarytime; + + if (namedVariable->typeSpec.binaryTime == 6) + typeSpec->choice.binarytime = 1; + else + typeSpec->choice.binarytime = 0; + + break; + default: + if (DEBUG_MMS_SERVER) + printf("MMS-SERVER: Unsupported type %i!\n", namedVariable->type); + return -1; + break; + } + } + + return 1; } static void freeTypeSpecRecursive(TypeSpecification_t* typeSpec) { - if (typeSpec->present == TypeSpecification_PR_structure) { - int elementCount = - typeSpec->choice.structure.components.list.count; - - int i; - - for (i = 0; i < elementCount; i++) { - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); - freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); - } - else if (typeSpec->present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); - freeTypeSpecRecursive(typeSpec->choice.array.elementType); - GLOBAL_FREEMEM(typeSpec->choice.array.elementType); - } + if (typeSpec->present == TypeSpecification_PR_structure) { + int elementCount = + typeSpec->choice.structure.components.list.count; + + int i; + + for (i = 0; i < elementCount; i++) { + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentName); + freeTypeSpecRecursive(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]->componentType); + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(typeSpec->choice.structure.components.list.array); + } + else if (typeSpec->present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(typeSpec->choice.array.numberOfElements.buf); + freeTypeSpecRecursive(typeSpec->choice.array.elementType); + GLOBAL_FREEMEM(typeSpec->choice.array.elementType); + } } static void deleteVariableAccessAttributesResponse( - GetVariableAccessAttributesResponse_t* getVarAccessAttr) + GetVariableAccessAttributesResponse_t* getVarAccessAttr) { - if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { - int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; - - int i; - for (i = 0; i < count; i++) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); - TypeSpecification_t* typeSpec = - getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; - freeTypeSpecRecursive(typeSpec); - GLOBAL_FREEMEM(typeSpec); - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); - } - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); - - getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; - getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; - getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; - } - else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; - getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; - freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); - - GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); - - getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; - } + if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_structure) { + int count = getVarAccessAttr->typeSpecification.choice.structure.components.list.count; + + int i; + for (i = 0; i < count; i++) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName->buf); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentName); + TypeSpecification_t* typeSpec = + getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]->componentType; + freeTypeSpecRecursive(typeSpec); + GLOBAL_FREEMEM(typeSpec); + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array[i]); + } + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.structure.components.list.array); + + getVarAccessAttr->typeSpecification.choice.structure.components.list.array = NULL; + getVarAccessAttr->typeSpecification.choice.structure.components.list.count = 0; + getVarAccessAttr->typeSpecification.choice.structure.components.list.size = 0; + } + else if (getVarAccessAttr->typeSpecification.present == TypeSpecification_PR_array) { + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf); + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.buf = NULL; + getVarAccessAttr->typeSpecification.choice.array.numberOfElements.size = 0; + freeTypeSpecRecursive(getVarAccessAttr->typeSpecification.choice.array.elementType); + + GLOBAL_FREEMEM(getVarAccessAttr->typeSpecification.choice.array.elementType); + + getVarAccessAttr->typeSpecification.choice.array.elementType = NULL; + } } static void createVariableAccessAttributesResponse( - MmsServerConnection connection, - char* domainId, - char* nameId, - int invokeId, - ByteBuffer* response) + MmsServerConnection connection, + char* domainId, + char* nameId, + int invokeId, + ByteBuffer* response) { - MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDevice* device = MmsServer_getDevice(connection->server); - MmsVariableSpecification* namedVariable = NULL; + MmsVariableSpecification* namedVariable = NULL; - MmsDomain* domain = NULL; + MmsDomain* domain = NULL; - if (domainId != NULL) { - domain = MmsDevice_getDomain(device, domainId); + if (domainId != NULL) { + domain = MmsDevice_getDomain(device, domainId); - if (domain == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); + if (domain == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: domain %s not known\n", domainId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + goto exit_function; + } - namedVariable = MmsDomain_getNamedVariable(domain, nameId); - } + namedVariable = MmsDomain_getNamedVariable(domain, nameId); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else - namedVariable = MmsDevice_getNamedVariable(device, nameId); + else + namedVariable = MmsDevice_getNamedVariable(device, nameId); #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - if (namedVariable == NULL) { - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); + if (namedVariable == NULL) { + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: named variable %s.%s not known\n", domainId, nameId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + goto exit_function; + } - bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); + bool accessAllowed = mmsServer_checkListAccess(connection->server, domain, nameId, connection); - if (!accessAllowed) { - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); + if (!accessAllowed) { + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: named variable %s/%s not visible due to access restrictions\n", domainId, nameId); - mmsMsg_createServiceErrorPdu(invokeId, response, - MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); + mmsMsg_createServiceErrorPdu(invokeId, response, + MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); - goto exit_function; - } + goto exit_function; + } - MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); + MmsPdu_t* mmsPdu = mmsServer_createConfirmedResponse(invokeId); - mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = - ConfirmedServiceResponse_PR_getVariableAccessAttributes; + mmsPdu->choice.confirmedResponsePdu.confirmedServiceResponse.present = + ConfirmedServiceResponse_PR_getVariableAccessAttributes; - GetVariableAccessAttributesResponse_t* getVarAccessAttr; + GetVariableAccessAttributesResponse_t* getVarAccessAttr; - getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. - confirmedServiceResponse.choice.getVariableAccessAttributes); + getVarAccessAttr = &(mmsPdu->choice.confirmedResponsePdu. + confirmedServiceResponse.choice.getVariableAccessAttributes); - getVarAccessAttr->mmsDeletable = 0; + getVarAccessAttr->mmsDeletable = 0; - createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); + createTypeSpecification(namedVariable, &getVarAccessAttr->typeSpecification); - asn_enc_rval_t rval = - der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); + asn_enc_rval_t rval = + der_encode(&asn_DEF_MmsPdu, mmsPdu, mmsServer_write_out, (void*) response); - if (rval.encoded == -1) { - response->size = 0; + if (rval.encoded == -1) { + response->size = 0; if (DEBUG_MMS_SERVER) printf("MMS getVariableAccessAttributes: message to large! send error PDU!\n"); @@ -284,81 +284,80 @@ createVariableAccessAttributesResponse( MMS_ERROR_SERVICE_OTHER); goto exit_function; - } + } - deleteVariableAccessAttributesResponse(getVarAccessAttr); + deleteVariableAccessAttributesResponse(getVarAccessAttr); - asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); + asn_DEF_MmsPdu.free_struct(&asn_DEF_MmsPdu, mmsPdu, 0); exit_function: - return; + return; } int mmsServer_handleGetVariableAccessAttributesRequest( - MmsServerConnection connection, - uint8_t* buffer, int bufPos, int maxBufPos, - uint32_t invokeId, - ByteBuffer* response) + MmsServerConnection connection, + uint8_t* buffer, int bufPos, int maxBufPos, + uint32_t invokeId, + ByteBuffer* response) { - int retVal = 0; + int retVal = 0; - GetVariableAccessAttributesRequest_t* request = 0; + GetVariableAccessAttributesRequest_t* request = 0; - asn_dec_rval_t rval; /* Decoder return value */ + asn_dec_rval_t rval; /* Decoder return value */ - rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, - (void**) &request, buffer + bufPos, maxBufPos - bufPos); + rval = ber_decode(NULL, &asn_DEF_GetVariableAccessAttributesRequest, + (void**) &request, buffer + bufPos, maxBufPos - bufPos); - if (rval.code == RC_OK) { - if (request->present == GetVariableAccessAttributesRequest_PR_name) { - if (request->choice.name.present == ObjectName_PR_domainspecific) { - Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; - Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; + if (rval.code == RC_OK) { + if (request->present == GetVariableAccessAttributesRequest_PR_name) { + if (request->choice.name.present == ObjectName_PR_domainspecific) { + Identifier_t domainId = request->choice.name.choice.domainspecific.domainId; + Identifier_t nameId = request->choice.name.choice.domainspecific.itemId; - char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* domainIdStr = StringUtils_createStringFromBuffer(domainId.buf, domainId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) - printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getVariableAccessAttributes domainId: %s nameId: %s\n", domainIdStr, nameIdStr); - createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, domainIdStr, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(domainIdStr); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(domainIdStr); + GLOBAL_FREEMEM(nameIdStr); + } #if (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) - else if (request->choice.name.present == ObjectName_PR_vmdspecific) { - Identifier_t nameId = request->choice.name.choice.vmdspecific; + else if (request->choice.name.present == ObjectName_PR_vmdspecific) { + Identifier_t nameId = request->choice.name.choice.vmdspecific; - char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); + char* nameIdStr = StringUtils_createStringFromBuffer(nameId.buf, nameId.size); - if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getVariableAccessAttributes (VMD specific) nameId: %s\n", nameIdStr); - createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); + createVariableAccessAttributesResponse(connection, NULL, nameIdStr, invokeId, response); - GLOBAL_FREEMEM(nameIdStr); - } + GLOBAL_FREEMEM(nameIdStr); + } #endif /* (CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES == 1) */ - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); - retVal = -1; - } - } - else { - if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); - retVal = -1; - } - - asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); - - return retVal; + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with name other than domainspecific is not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest with address not supported!\n"); + retVal = -1; + } + } + else { + if (DEBUG_MMS_SERVER) printf("GetVariableAccessAttributesRequest parsing request failed!\n"); + retVal = -1; + } + + asn_DEF_GetVariableAccessAttributesRequest.free_struct(&asn_DEF_GetVariableAccessAttributesRequest, request, 0); + + return retVal; } #endif /* (MMS_GET_VARIABLE_ACCESS_ATTRIBUTES == 1) */ - From 98c04dfeda5c81a5f80ed4c9ce8ef713e93b323c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 28 Jul 2023 15:55:20 +0100 Subject: [PATCH 060/214] - added LISTOBEJCTS access control to server_example_access_control (LIB61850-417) --- .../server_example_access_control.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/server_example_access_control/server_example_access_control.c b/examples/server_example_access_control/server_example_access_control.c index e0b789af..365576e8 100644 --- a/examples/server_example_access_control/server_example_access_control.c +++ b/examples/server_example_access_control/server_example_access_control.c @@ -146,6 +146,20 @@ readAccessHandler(LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, Fu return DATA_ACCESS_ERROR_SUCCESS; } +static bool +listObjectsAccessHandler(void* parameter, ClientConnection connection, LogicalDevice* ld, LogicalNode* ln, DataObject* dataObject, FunctionalConstraint fc) +{ + printf("list objects access to %s/%s.%s[%s]\n", ld->name, ln->name, dataObject ? dataObject->name : "-", FunctionalConstraint_toString(fc)); + + if (!strcmp(ln->name, "GGIO1")) { + if (dataObject && !strcmp(dataObject->name, "AnIn1")) { + return false; + } + } + + return true; +} + static bool directoryAccessHandler(void* parameter, ClientConnection connection, IedServer_DirectoryCategory category, LogicalDevice* logicalDevice) { @@ -258,6 +272,9 @@ main(int argc, char** argv) IedServer_setDirectoryAccessHandler(iedServer, directoryAccessHandler, NULL); + /* control visibility of data objects in directory (get-name-list) and variable description (get-variable-access-attributes) services */ + IedServer_setListObjectsAccessHandler(iedServer, listObjectsAccessHandler, NULL); + /* MMS server will be instructed to start listening for client connections. */ IedServer_start(iedServer, tcpPort); From 5a24981048dc063bc96c3f14400ff4b5bc2df093 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 30 Jul 2023 19:01:32 +0100 Subject: [PATCH 061/214] - IED server: added code to create SMVCBs with the dynamic model API (LIB61850-67) --- src/iec61850/server/model/dynamic_model.c | 33 +++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/iec61850/server/model/dynamic_model.c b/src/iec61850/server/model/dynamic_model.c index 790c9be3..68710320 100644 --- a/src/iec61850/server/model/dynamic_model.c +++ b/src/iec61850/server/model/dynamic_model.c @@ -166,7 +166,6 @@ IedModel_addSettingGroupControlBlock(IedModel* self, SettingGroupControlBlock* s } #endif /* (CONFIG_IEC61850_SETTING_GROUPS == 1) */ - static void IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb) { @@ -175,13 +174,29 @@ IedModel_addGSEControlBlock(IedModel* self, GSEControlBlock* gcb) else { GSEControlBlock* lastGcb = self->gseCBs; - while (lastGcb->sibling != NULL) + while (lastGcb->sibling) lastGcb = lastGcb->sibling; lastGcb->sibling = gcb; } } +static void +IedModel_addSMVControlBlock(IedModel* self, SVControlBlock* smvcb) +{ + if (self->svCBs == NULL) { + self->svCBs = smvcb; + } + else { + SVControlBlock* lastSvCB = self->svCBs; + + while (lastSvCB->sibling) + lastSvCB = lastSvCB->sibling; + + lastSvCB->sibling = smvcb; + } +} + LogicalDevice* LogicalDevice_createEx(const char* inst, IedModel* parent, const char* ldName) { @@ -512,6 +527,14 @@ GSEControlBlock_create(const char* name, LogicalNode* parent, const char* appId, return self; } +static void +LogicalNode_addSMVControlBlock(LogicalNode* self, SVControlBlock* smvcb) +{ + IedModel* model = (IedModel*) self->parent->parent; + + IedModel_addSMVControlBlock(model, smvcb); +} + SVControlBlock* SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, const char* dataSet, uint32_t confRev, uint8_t smpMod, uint16_t smpRate, uint8_t optFlds, bool isUnicast) @@ -536,6 +559,12 @@ SVControlBlock_create(const char* name, LogicalNode* parent, const char* svID, c self->optFlds = optFlds; self->isUnicast = isUnicast; + + self->dstAddress = NULL; + self->sibling = NULL; + + if (parent) + LogicalNode_addSMVControlBlock(parent, self); } return self; From 405124b8d90d3243121363a1008a3a82b50a220f Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 30 Jul 2023 19:04:39 +0100 Subject: [PATCH 062/214] - config file generator: added code to add SMVCBs to config files (LIB61850-67) --- .../scl/communication/ConnectedAP.java | 14 +++++- .../tools/DynamicModelGenerator.java | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java b/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java index d9765b04..b3cf608d 100644 --- a/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java +++ b/tools/model_generator/src/com/libiec61850/scl/communication/ConnectedAP.java @@ -85,7 +85,7 @@ public class ConnectedAP { public List getSmvs() { return smvs; } - + public GSE lookupGSE(String logicalDeviceName, String name) { for (GSE gse : this.getGses()) { @@ -97,6 +97,18 @@ public class ConnectedAP { return null; } + + public SMV lookupSMV(String logicalDeviceName, String name) { + + for (SMV smv : this.getSmvs()) { + if (smv.getLdInst().equals(logicalDeviceName)) { + if (smv.getCbName().equals(name)) + return smv; + } + } + + return null; + } public PhyComAddress lookupSMVAddress(String logicalDeviceName, String name) { diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java index cd1d4593..e94ca7ab 100644 --- a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java @@ -37,6 +37,7 @@ import com.libiec61850.scl.SclParserException; import com.libiec61850.scl.communication.ConnectedAP; import com.libiec61850.scl.communication.GSE; import com.libiec61850.scl.communication.PhyComAddress; +import com.libiec61850.scl.communication.SMV; import com.libiec61850.scl.model.AccessPoint; import com.libiec61850.scl.model.DataAttribute; import com.libiec61850.scl.model.DataModelValue; @@ -51,6 +52,7 @@ import com.libiec61850.scl.model.LogicalDevice; import com.libiec61850.scl.model.LogicalNode; import com.libiec61850.scl.model.ReportControlBlock; import com.libiec61850.scl.model.ReportSettings; +import com.libiec61850.scl.model.SampledValueControl; import com.libiec61850.scl.model.Services; import com.libiec61850.scl.model.SettingControl; @@ -166,6 +168,52 @@ public class DynamicModelGenerator { for (Log log : logicalNode.getLogs()) output.println("LOG(" + log.getName() + ");"); + + for (SampledValueControl svcb : logicalNode.getSampledValueControlBlocks()) { + LogicalDevice ld = logicalNode.getParentLogicalDevice(); + + SMV smv = null; + PhyComAddress smvAddress = null; + + if (connectedAP != null) { + smv = connectedAP.lookupSMV(ld.getInst(), svcb.getName()); + + if (smv != null) + smvAddress = smv.getAddress(); + } + else + System.out.println("WARNING: IED \"" + ied.getName() + "\" has no connected access point!"); + + output.print("SMVC("); + output.print(svcb.getName() + " "); + output.print(svcb.getSmvID() + " "); + output.print(svcb.getDatSet() + " "); + output.print(svcb.getConfRev() + " "); + output.print("0" + " "); + output.print(svcb.getSmpRate() + " "); + output.print(svcb.getSmvOpts().getIntValue() + " "); + output.print(svcb.isMulticast() ? "0" : "1"); + output.print(")"); + + if (smvAddress != null) { + output.println("{"); + + output.print("PA("); + output.print(smvAddress.getVlanPriority() + " "); + output.print(smvAddress.getVlanId() + " "); + output.print(smvAddress.getAppId() + " "); + + for (int i = 0; i < 6; i++) + output.printf("%02x", smvAddress.getMacAddress()[i]); + + output.println(");"); + + output.println("}"); + } + else { + output.println(";"); + } + } for (GSEControl gcb : logicalNode.getGSEControlBlocks()) { LogicalDevice ld = logicalNode.getParentLogicalDevice(); From 0622bae0f6eda6611352621bdc094f8b99fefa72 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 30 Jul 2023 20:02:37 +0100 Subject: [PATCH 063/214] - config file generator/static model generator: added support for SampledValueControl.smpMod (LIB61850-67) --- examples/iec61850_9_2_LE_example/sv.icd | 2 +- .../scl/model/SampledValueControl.java | 31 +++++++--- .../src/com/libiec61850/scl/model/SmpMod.java | 56 +++++++++++++++++++ .../tools/DynamicModelGenerator.java | 2 +- .../tools/StaticModelGenerator.java | 4 +- 5 files changed, 85 insertions(+), 10 deletions(-) create mode 100644 tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java diff --git a/examples/iec61850_9_2_LE_example/sv.icd b/examples/iec61850_9_2_LE_example/sv.icd index b52f8d92..af5fe962 100644 --- a/examples/iec61850_9_2_LE_example/sv.icd +++ b/examples/iec61850_9_2_LE_example/sv.icd @@ -93,7 +93,7 @@ SCL.xsd"> + smvID="xxxxMUnn01" smpRate="80" nofASDU="1" confRev="1" smpMod="SmpPerPeriod"> diff --git a/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java b/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java index f2f03b66..6ef8c29b 100644 --- a/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java +++ b/tools/model_generator/src/com/libiec61850/scl/model/SampledValueControl.java @@ -16,9 +16,8 @@ public class SampledValueControl { private int nofASDU; private boolean multicast = false; private SmvOpts smvOpts; - - - + private SmpMod smpMod = SmpMod.SMP_PER_PERIOD; + public SampledValueControl(Node smvControlNode) throws SclParserException { this.name = ParserUtils.parseAttribute(smvControlNode, "name"); this.desc = ParserUtils.parseAttribute(smvControlNode, "desc"); @@ -49,10 +48,25 @@ public class SampledValueControl { Node smvOptsNode = ParserUtils.getChildNodeWithTag(smvControlNode, "SmvOpts"); this.smvOpts = new SmvOpts(smvOptsNode); + + String smpModString = ParserUtils.parseAttribute(smvControlNode, "smpMod"); + + if (smpModString != null) { + if (smpModString.equals("SmpPerPeriod")) { + smpMod = SmpMod.SMP_PER_PERIOD; + } + else if (smpModString.equals("SmpPerSec")) { + smpMod = SmpMod.SMP_PER_SECOND; + } + else if (smpModString.equals("SecPerSmp")) { + smpMod = SmpMod.SEC_PER_SMP; + } + else { + throw new SclParserException(smvControlNode, "Invalid smpMod value " + smpModString); + } + } } - - - + public String getName() { return name; } @@ -88,5 +102,8 @@ public class SampledValueControl { public SmvOpts getSmvOpts() { return smvOpts; } - + + public SmpMod getSmpMod() { + return smpMod; + } } diff --git a/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java b/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java new file mode 100644 index 00000000..4de9f6eb --- /dev/null +++ b/tools/model_generator/src/com/libiec61850/scl/model/SmpMod.java @@ -0,0 +1,56 @@ +package com.libiec61850.scl.model; + +/* + * Copyright 2023 Michael Zillgith + * + * This file is part of libIEC61850. + * + * libIEC61850 is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * libIEC61850 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with libIEC61850. If not, see . + * + * See COPYING file for the complete license text. + */ + +public enum SmpMod +{ + SMP_PER_PERIOD(0), + SMP_PER_SECOND(1), + SEC_PER_SMP(2); + + private int value; + + private SmpMod(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public boolean compare(int i) + { + return (value == i); + } + + public static SmpMod fromValue(int val) + { + SmpMod[] errors = SmpMod.values(); + + for (int i = 0; i < errors.length; i++) { + if (errors[i].compare(val)) + return errors[i]; + } + + return SmpMod.SMP_PER_PERIOD; + } +} diff --git a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java index e94ca7ab..0c05fd57 100644 --- a/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/DynamicModelGenerator.java @@ -189,7 +189,7 @@ public class DynamicModelGenerator { output.print(svcb.getSmvID() + " "); output.print(svcb.getDatSet() + " "); output.print(svcb.getConfRev() + " "); - output.print("0" + " "); + output.print(svcb.getSmpMod().getValue() + " "); output.print(svcb.getSmpRate() + " "); output.print(svcb.getSmvOpts().getIntValue() + " "); output.print(svcb.isMulticast() ? "0" : "1"); diff --git a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java index 4381aff0..a06313a0 100644 --- a/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java +++ b/tools/model_generator/src/com/libiec61850/tools/StaticModelGenerator.java @@ -1247,8 +1247,10 @@ public class StaticModelGenerator { svString += "NULL, "; svString += svCB.getSmvOpts().getIntValue() + ", "; + + svString += svCB.getSmpMod().getValue() + ", "; - svString += "0, " + svCB.getSmpRate() + ", "; + svString += svCB.getSmpRate() + ", "; svString += svCB.getConfRev() + ", "; From 15359059212929e1d2594a081c2a02aaf0363155 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 30 Jul 2023 20:06:42 +0100 Subject: [PATCH 064/214] - added updated compiled versions of config file and static model generator (LIB61850-67) --- tools/model_generator/genconfig.jar | Bin 97408 -> 100509 bytes tools/model_generator/genmodel.jar | Bin 98733 -> 100549 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/tools/model_generator/genconfig.jar b/tools/model_generator/genconfig.jar index 2c9ed1fc32bfed1a96eac95456e1f376101d23df..6f42a2ba6247a31d4ea4ed573b23e23305214280 100644 GIT binary patch delta 40581 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

>CojhY(G()abf3Sm<%HU}%UNlAqRU=HF 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