From fe59285e807234ad08070b4ab50ce9ee51cc38ea Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 23 Jun 2018 10:42:53 +0200 Subject: [PATCH 01/24] - CS104 slave: added support for non-thread mode --- lib60870-C/examples/CMakeLists.txt | 1 + .../cs104_server_no_threads/CMakeLists.txt | 20 + .../examples/cs104_server_no_threads/Makefile | 20 + .../cs104_server_no_threads.c | 285 ++++++++++++++ lib60870-C/src/iec60870/cs104/cs104_slave.c | 349 ++++++++++++++++-- lib60870-C/src/inc/api/cs104_slave.h | 10 + 6 files changed, 651 insertions(+), 34 deletions(-) create mode 100644 lib60870-C/examples/cs104_server_no_threads/CMakeLists.txt create mode 100644 lib60870-C/examples/cs104_server_no_threads/Makefile create mode 100644 lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c diff --git a/lib60870-C/examples/CMakeLists.txt b/lib60870-C/examples/CMakeLists.txt index 8a3254e7..73bc3702 100644 --- a/lib60870-C/examples/CMakeLists.txt +++ b/lib60870-C/examples/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(cs101_master_unbalanced) add_subdirectory(cs101_slave) add_subdirectory(cs104_client) add_subdirectory(cs104_server) +add_subdirectory(cs104_server_no_threads) add_subdirectory(multi_client_server) if (WITH_MBEDTLS) diff --git a/lib60870-C/examples/cs104_server_no_threads/CMakeLists.txt b/lib60870-C/examples/cs104_server_no_threads/CMakeLists.txt new file mode 100644 index 00000000..5b846429 --- /dev/null +++ b/lib60870-C/examples/cs104_server_no_threads/CMakeLists.txt @@ -0,0 +1,20 @@ +include_directories( + . +) + +set(example_SRCS + cs104_server_no_threads.c +) + +IF(WIN32) +set_source_files_properties(${example_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(WIN32) + +add_executable(cs104_server_no_threads + ${example_SRCS} +) + +target_link_libraries(cs104_server_no_threads + lib60870 +) diff --git a/lib60870-C/examples/cs104_server_no_threads/Makefile b/lib60870-C/examples/cs104_server_no_threads/Makefile new file mode 100644 index 00000000..c329a6d3 --- /dev/null +++ b/lib60870-C/examples/cs104_server_no_threads/Makefile @@ -0,0 +1,20 @@ +LIB60870_HOME=../.. + +PROJECT_BINARY_NAME = cs104_server_no_threads +PROJECT_SOURCES = cs104_server_no_threads.c + +include $(LIB60870_HOME)/make/target_system.mk +include $(LIB60870_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIB60870_HOME)/make/common_targets.mk + + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -g -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) + + diff --git a/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c b/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c new file mode 100644 index 00000000..795f4a80 --- /dev/null +++ b/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include + +#include "cs104_slave.h" + +#include "hal_thread.h" +#include "hal_time.h" + +static bool running = true; + +void +sigint_handler(int signalId) +{ + running = false; +} + +void +printCP56Time2a(CP56Time2a time) +{ + printf("%02i:%02i:%02i %02i/%02i/%04i", CP56Time2a_getHour(time), + CP56Time2a_getMinute(time), + CP56Time2a_getSecond(time), + CP56Time2a_getDayOfMonth(time), + CP56Time2a_getMonth(time), + CP56Time2a_getYear(time) + 2000); +} + +/* Callback handler to log sent or received messages (optional) */ +static void +rawMessageHandler(void* parameter, IMasterConnection conneciton, uint8_t* msg, int msgSize, bool sent) +{ + if (sent) + printf("SEND: "); + else + printf("RCVD: "); + + int i; + for (i = 0; i < msgSize; i++) { + printf("%02x ", msg[i]); + } + + printf("\n"); +} + +static bool +clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime) +{ + printf("Process time sync command with time "); printCP56Time2a(newTime); printf("\n"); + + return true; +} + +static bool +interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) +{ + printf("Received interrogation for group %i\n", qoi); + + if (qoi == 20) { /* only handle station interrogation */ + + CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection); + + IMasterConnection_sendACT_CON(connection, asdu, false); + + /* The CS101 specification only allows information objects without timestamp in GI responses */ + + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, + 0, 1, false, false); + + InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 100, -1, IEC60870_QUALITY_GOOD); + + CS101_ASDU_addInformationObject(newAsdu, io); + + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + MeasuredValueScaled_create((MeasuredValueScaled) io, 101, 23, IEC60870_QUALITY_GOOD)); + + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + MeasuredValueScaled_create((MeasuredValueScaled) io, 102, 2300, IEC60870_QUALITY_GOOD)); + + InformationObject_destroy(io); + + IMasterConnection_sendASDU(connection, newAsdu); + + CS101_ASDU_destroy(newAsdu); + + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, + 0, 1, false, false); + + io = (InformationObject) SinglePointInformation_create(NULL, 104, true, IEC60870_QUALITY_GOOD); + + CS101_ASDU_addInformationObject(newAsdu, io); + + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + SinglePointInformation_create((SinglePointInformation) io, 105, false, IEC60870_QUALITY_GOOD)); + + InformationObject_destroy(io); + + IMasterConnection_sendASDU(connection, newAsdu); + + CS101_ASDU_destroy(newAsdu); + + newAsdu = CS101_ASDU_create(alParams, true, CS101_COT_INTERROGATED_BY_STATION, + 0, 1, false, false); + + CS101_ASDU_addInformationObject(newAsdu, io = (InformationObject) SinglePointInformation_create(NULL, 300, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 301, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 302, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 303, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 304, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 305, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 306, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 307, false, IEC60870_QUALITY_GOOD)); + + InformationObject_destroy(io); + + IMasterConnection_sendASDU(connection, newAsdu); + + CS101_ASDU_destroy(newAsdu); + + IMasterConnection_sendACT_TERM(connection, asdu); + } + else { + IMasterConnection_sendACT_CON(connection, asdu, true); + } + + return true; +} + +static bool +asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu) +{ + if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) { + printf("received single command\n"); + + if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) { + InformationObject io = CS101_ASDU_getElement(asdu, 0); + + if (InformationObject_getObjectAddress(io) == 5000) { + SingleCommand sc = (SingleCommand) io; + + printf("IOA: %i switch to %i\n", InformationObject_getObjectAddress(io), + SingleCommand_getState(sc)); + + CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); + } + else + CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA); + + InformationObject_destroy(io); + } + else + CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT); + + IMasterConnection_sendASDU(connection, asdu); + + return true; + } + + return false; +} + +static bool +connectionRequestHandler(void* parameter, const char* ipAddress) +{ + printf("New connection request from %s\n", ipAddress); + +#if 0 + if (strcmp(ipAddress, "127.0.0.1") == 0) { + printf("Accept connection\n"); + return true; + } + else { + printf("Deny connection\n"); + return false; + } +#else + return true; +#endif +} + +static void +connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event) +{ + if (event == CS104_CON_EVENT_CONNECTION_OPENED) { + printf("Connection opened (%p)\n", con); + } + else if (event == CS104_CON_EVENT_CONNECTION_CLOSED) { + printf("Connection closed (%p)\n", con); + } + else if (event == CS104_CON_EVENT_ACTIVATED) { + printf("Connection activated (%p)\n", con); + } + else if (event == CS104_CON_EVENT_DEACTIVATED) { + printf("Connection deactivated (%p)\n", con); + } +} + +int +main(int argc, char** argv) +{ + /* Add Ctrl-C handler */ + signal(SIGINT, sigint_handler); + + /* create a new slave/server instance with default connection parameters and + * default message queue size */ + CS104_Slave slave = CS104_Slave_create(100, 100); + + CS104_Slave_setLocalAddress(slave, "0.0.0.0"); + + /* Set mode to a single redundancy group + * NOTE: library has to be compiled with CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP enabled (=1) + */ + CS104_Slave_setServerMode(slave, CS104_MODE_SINGLE_REDUNDANCY_GROUP); + + /* get the connection parameters - we need them to create correct ASDUs */ + CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave); + + /* set the callback handler for the clock synchronization command */ + CS104_Slave_setClockSyncHandler(slave, clockSyncHandler, NULL); + + /* set the callback handler for the interrogation command */ + CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL); + + /* set handler for other message types */ + CS104_Slave_setASDUHandler(slave, asduHandler, NULL); + + /* set handler to handle connection requests (optional) */ + CS104_Slave_setConnectionRequestHandler(slave, connectionRequestHandler, NULL); + + /* set handler to track connection events (optional) */ + CS104_Slave_setConnectionEventHandler(slave, connectionEventHandler, NULL); + + /* uncomment to log messages */ + //CS104_Slave_setRawMessageHandler(slave, rawMessageHandler, NULL); + + CS104_Slave_startThreadless(slave); + + if (CS104_Slave_isRunning(slave) == false) { + printf("Starting server failed!\n"); + goto exit_program; + } + + int16_t scaledValue = 0; + + uint64_t nextSendTime = Hal_getTimeInMs() + 1000; + + while (running) { + + CS104_Slave_tick(slave); + + if (Hal_getTimeInMs() >= nextSendTime) { + + nextSendTime += 1000; + + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + + InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 110, scaledValue, IEC60870_QUALITY_GOOD); + + scaledValue++; + + CS101_ASDU_addInformationObject(newAsdu, io); + + InformationObject_destroy(io); + + /* Add ASDU to slave event queue - don't release the ASDU afterwards! + * The ASDU will be released by the Slave instance when the ASDU + * has been sent. + */ + CS104_Slave_enqueueASDU(slave, newAsdu); + + CS101_ASDU_destroy(newAsdu); + } + + Thread_sleep(1); + } + + CS104_Slave_stopThreadless(slave); + +exit_program: + CS104_Slave_destroy(slave); + + Thread_sleep(500); +} diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index a7f89396..21dab00e 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -636,9 +636,11 @@ struct sCS104_Slave { int maxHighPrioQueueSize; int openConnections; /**< number of connected clients */ + LinkedList masterConnections; /**< references to all MasterConnection objects */ #if (CONFIG_USE_THREADS == 1) Semaphore openConnectionsLock; + bool isThreadlessMode; #endif int maxOpenConnections; /**< maximum accepted open client connections */ @@ -660,6 +662,8 @@ struct sCS104_Slave { char* localAddress; Thread listeningThread; + + ServerSocket serverSocket; }; #if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) @@ -746,6 +750,7 @@ createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) self->maxOpenConnections = CONFIG_CS104_MAX_CLIENT_CONNECTIONS; #if (CONFIG_USE_THREADS == 1) self->openConnectionsLock = Semaphore_create(1); + self->isThreadlessMode = false; #endif self->isRunning = false; @@ -756,6 +761,8 @@ createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) self->openConnections = 0; self->listeningThread = NULL; + self->serverSocket = NULL; + #if (CONFIG_CS104_SUPPORT_TLS == 1) self->tlsConfig = NULL; #endif @@ -998,6 +1005,10 @@ struct sMasterConnection { Semaphore sentASDUsLock; #endif + HandleSet handleSet; + + uint8_t buffer[260]; + MessageQueue lowPrioQueue; HighPriorityASDUQueue highPrioQueue; }; @@ -1652,6 +1663,8 @@ MasterConnection_destroy(MasterConnection self) Semaphore_destroy(self->sentASDUsLock); #endif + Handleset_destroy(self->handleSet); + GLOBAL_FREEMEM(self); } } @@ -1840,14 +1853,10 @@ connectionHandlingThread(void* parameter) { MasterConnection self = (MasterConnection) parameter; - uint8_t buffer[260]; - self->isRunning = true; resetT3Timeout(self); - HandleSet handleSet = Handleset_new(); - bool isAsduWaiting = false; if (self->slave->connectionEventHandler) { @@ -1856,8 +1865,8 @@ connectionHandlingThread(void* parameter) while (self->isRunning) { - Handleset_reset(handleSet); - Handleset_addSocket(handleSet, self->socket); + Handleset_reset(self->handleSet); + Handleset_addSocket(self->handleSet, self->socket); int socketTimeout; @@ -1870,13 +1879,13 @@ connectionHandlingThread(void* parameter) else socketTimeout = 100; /* TODO replace by configurable parameter */ - if (Handleset_waitReady(handleSet, socketTimeout)) { + if (Handleset_waitReady(self->handleSet, socketTimeout)) { - int bytesRec = receiveMessage(self, buffer); + int bytesRec = receiveMessage(self, self->buffer); if (self->slave->rawMessageHandler) self->slave->rawMessageHandler(self->slave->rawMessageHandlerParameter, - &(self->iMasterConnection), buffer, bytesRec, false); + &(self->iMasterConnection), self->buffer, bytesRec, false); if (bytesRec == -1) { DEBUG_PRINT("Error reading from socket\n"); @@ -1886,7 +1895,7 @@ connectionHandlingThread(void* parameter) if (bytesRec > 0) { DEBUG_PRINT("Connection: rcvd msg(%i bytes)\n", bytesRec); - if (handleMessage(self, buffer, bytesRec) == false) + if (handleMessage(self, self->buffer, bytesRec) == false) self->isRunning = false; if (self->unconfirmedReceivedIMessages >= self->slave->conParameters.w) { @@ -1916,8 +1925,6 @@ connectionHandlingThread(void* parameter) DEBUG_PRINT("Connection closed\n"); - Handleset_destroy(handleSet); - self->isRunning = false; #if (CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP == 1) @@ -2004,6 +2011,8 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu self->iMasterConnection.sendACT_CON = _sendACT_CON; self->iMasterConnection.sendACT_TERM = _sendACT_TERM; + resetT3Timeout(self); + #if (CONFIG_USE_THREADS == 1) self->sentASDUsLock = Semaphore_create(1); #endif @@ -2027,6 +2036,8 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu self->highPrioQueue = highPrioQueue; self->outstandingTestFRConMessages = 0; + + self->handleSet = Handleset_new(); } return self; @@ -2072,33 +2083,235 @@ MasterConnection_activate(MasterConnection self) self->isActive = true; } +static void +MasterConnection_handleTcpConnection(MasterConnection self) +{ + int bytesRec = receiveMessage(self, self->buffer); + + if (self->slave->rawMessageHandler) + self->slave->rawMessageHandler(self->slave->rawMessageHandlerParameter, + &(self->iMasterConnection), self->buffer, bytesRec, false); + + if (bytesRec == -1) { + DEBUG_PRINT("Error reading from socket\n"); + self->isRunning = false; + } + + if (bytesRec > 0) { + DEBUG_PRINT("Connection: rcvd msg(%i bytes)\n", bytesRec); + + if (handleMessage(self, self->buffer, bytesRec) == false) + self->isRunning = false; + + if (self->unconfirmedReceivedIMessages >= self->slave->conParameters.w) { + + self->lastConfirmationTime = Hal_getTimeInMs(); + + self->unconfirmedReceivedIMessages = 0; + + self->timeoutT2Triggered = false; + + sendSMessage(self); + } + } + +} + +static void +MasterConnection_executePeriodicTasks(MasterConnection self) +{ + if (self->isActive) + sendWaitingASDUs(self); + + if (handleTimeouts(self) == false) + self->isRunning = false; +} + +static void +handleClientConnections(CS104_Slave self) +{ + HandleSet handleset = NULL; + + if (self->openConnections > 0) { + + LinkedList connection = LinkedList_getNext(self->masterConnections); + + bool first = true; + while (connection) { + MasterConnection con = (MasterConnection) LinkedList_getData(connection); + + if (con->isRunning) { + + connection = LinkedList_getNext(connection); + + if (first) { + handleset = con->handleSet; + Handleset_reset(handleset); + first = false; + } + + Handleset_addSocket(handleset, con->socket); + } + else { + + connection = LinkedList_getNext(connection); + + + if (self->connectionEventHandler) { + self->connectionEventHandler(self->connectionEventHandlerParameter, &(con->iMasterConnection), CS104_CON_EVENT_CONNECTION_CLOSED); + } + + DEBUG_PRINT("Connection closed\n"); + + LinkedList_remove(self->masterConnections, con); + + self->openConnections--; + + MasterConnection_destroy(con); + } + + } + + if (handleset != NULL) { + + + if (Handleset_waitReady(handleset, 1)) { + connection = LinkedList_getNext(self->masterConnections); + + while (connection) { + + MasterConnection con = (MasterConnection) LinkedList_getData(connection); + + MasterConnection_handleTcpConnection(con); + + connection = LinkedList_getNext(connection); + } + } + } + + + connection = LinkedList_getNext(self->masterConnections); + + while (connection) { + + MasterConnection con = (MasterConnection) LinkedList_getData(connection); + + MasterConnection_executePeriodicTasks(con); + + connection = LinkedList_getNext(connection); + } + } + +} + +/* handle TCP connections in non-threaded mode */ +static void +handleConnectionsThreadless(CS104_Slave self) +{ + + + if ((self->maxOpenConnections < 1) || (self->openConnections < self->maxOpenConnections)) { + + Socket newSocket = ServerSocket_accept(self->serverSocket); + + if (newSocket != NULL) { + + bool acceptConnection = true; + + if (acceptConnection && (self->connectionRequestHandler != NULL)) { + char ipAddress[60]; + + Socket_getPeerAddressStatic(newSocket, ipAddress); + + /* remove TCP port part */ + char* separator = strchr(ipAddress, ':'); + if (separator != NULL) + *separator = 0; + + acceptConnection = self->connectionRequestHandler(self->connectionRequestHandlerParameter, + ipAddress); + } + + if (acceptConnection) { + + MessageQueue lowPrioQueue = NULL; + HighPriorityASDUQueue highPrioQueue = NULL; + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) + if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) { + lowPrioQueue = self->asduQueue; + highPrioQueue = self->connectionAsduQueue; + } +#endif + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP == 1) + if (self->serverMode == CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP) { + lowPrioQueue = MessageQueue_create(self->maxLowPrioQueueSize); + highPrioQueue = HighPriorityASDUQueue_create(self->maxHighPrioQueueSize); + } +#endif + + MasterConnection connection = + MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); + + if (connection) { + +#if (CONFIG_USE_THREADS) + Semaphore_wait(self->openConnectionsLock); +#endif + + self->openConnections++; + LinkedList_add(self->masterConnections, connection); + +#if (CONFIG_USE_THREADS) + Semaphore_post(self->openConnectionsLock); +#endif + + + + /* now start the connection handling (thread) */ + //MasterConnection_start(connection); + connection->isRunning = true; + } + else + DEBUG_PRINT("Connection attempt failed!"); + + } + else { + Socket_destroy(newSocket); + } + } + + } + + + handleClientConnections(self); +} static void* serverThread (void* parameter) { CS104_Slave self = (CS104_Slave) parameter; - ServerSocket serverSocket; - if (self->localAddress) - serverSocket = TcpServerSocket_create(self->localAddress, self->tcpPort); + self->serverSocket = TcpServerSocket_create(self->localAddress, self->tcpPort); else - serverSocket = TcpServerSocket_create("0.0.0.0", self->tcpPort); + self->serverSocket = TcpServerSocket_create("0.0.0.0", self->tcpPort); - if (serverSocket == NULL) { + if (self->serverSocket == NULL) { DEBUG_PRINT("Cannot create server socket\n"); self->isStarting = false; goto exit_function; } - ServerSocket_listen(serverSocket); + ServerSocket_listen(self->serverSocket); self->isRunning = true; self->isStarting = false; while (self->stopRunning == false) { - Socket newSocket = ServerSocket_accept(serverSocket); + Socket newSocket = ServerSocket_accept(self->serverSocket); if (newSocket != NULL) { @@ -2174,8 +2387,8 @@ serverThread (void* parameter) Thread_sleep(10); } - if (serverSocket) - Socket_destroy((Socket) serverSocket); + if (self->serverSocket) + Socket_destroy((Socket) self->serverSocket); self->isRunning = false; self->stopRunning = false; @@ -2227,7 +2440,6 @@ CS104_Slave_start(CS104_Slave self) if (self->isRunning == false) { self->isStarting = true; - self->isRunning = false; self->stopRunning = false; #if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) @@ -2243,6 +2455,56 @@ CS104_Slave_start(CS104_Slave self) } } +void +CS104_Slave_startThreadless(CS104_Slave self) +{ + if (self->isRunning == false) { + + self->isThreadlessMode = true; + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) + if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) + initializeMessageQueues(self, self->maxLowPrioQueueSize, self->maxHighPrioQueueSize); +#endif + + if (self->localAddress) + self->serverSocket = TcpServerSocket_create(self->localAddress, self->tcpPort); + else + self->serverSocket = TcpServerSocket_create("0.0.0.0", self->tcpPort); + + if (self->serverSocket == NULL) { + DEBUG_PRINT("Cannot create server socket\n"); + self->isStarting = false; + goto exit_function; + } + + ServerSocket_listen(self->serverSocket); + + self->isRunning = true; + } + +exit_function: + return; +} + +void +CS104_Slave_stopThreadless(CS104_Slave self) +{ + self->isRunning = false; + + if (self->serverSocket) { + Socket_destroy((Socket) self->serverSocket); + self->serverSocket = NULL; + } +} + +void +CS104_Slave_tick(CS104_Slave self) +{ + handleConnectionsThreadless(self); +} + + bool CS104_Slave_isRunning(CS104_Slave self) { @@ -2252,25 +2514,32 @@ CS104_Slave_isRunning(CS104_Slave self) void CS104_Slave_stop(CS104_Slave self) { - if (self->isRunning) { - self->stopRunning = true; - - while (self->isRunning) - Thread_sleep(1); + if (self->isThreadlessMode) { + CS104_Slave_stopThreadless(self); } + else { + if (self->isRunning) { + self->stopRunning = true; - if (self->listeningThread) { - Thread_destroy(self->listeningThread); - } + while (self->isRunning) + Thread_sleep(1); + } - self->listeningThread = NULL; + if (self->listeningThread) { + Thread_destroy(self->listeningThread); + } + + self->listeningThread = NULL; + } } void CS104_Slave_destroy(CS104_Slave self) { +#if (CONFIG_USE_THREADS == 1) if (self->isRunning) CS104_Slave_stop(self); +#endif #if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) @@ -2302,9 +2571,21 @@ CS104_Slave_destroy(CS104_Slave self) Semaphore_post(self->openConnectionsLock); #endif - /* Wait until all connections are closed */ - while (CS104_Slave_getOpenConnections(self) > 0) - Thread_sleep(10); + if (self->isThreadlessMode) { + for (element = LinkedList_getNext(self->masterConnections); + element != NULL; + element = LinkedList_getNext(element)) + { + MasterConnection connection = (MasterConnection) LinkedList_getData(element); + + MasterConnection_destroy(connection); + } + } + else { + /* Wait until all connections are closed */ + while (CS104_Slave_getOpenConnections(self) > 0) + Thread_sleep(10); + } LinkedList_destroyStatic(self->masterConnections); diff --git a/lib60870-C/src/inc/api/cs104_slave.h b/lib60870-C/src/inc/api/cs104_slave.h index 2e88705f..2a0be45f 100644 --- a/lib60870-C/src/inc/api/cs104_slave.h +++ b/lib60870-C/src/inc/api/cs104_slave.h @@ -241,6 +241,16 @@ CS104_Slave_isRunning(CS104_Slave self); void CS104_Slave_stop(CS104_Slave self); + +void +CS104_Slave_startThreadless(CS104_Slave self); + +void +CS104_Slave_stopThreadless(CS104_Slave self); + +void +CS104_Slave_tick(CS104_Slave self); + /** * \brief Add an ASDU to the low-priority queue of the slave (use for periodic and spontaneous messages) * From 9a6e41aa05b2d9da0054edf2ec9353a5eb8c6a2d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 23 Jun 2018 11:11:59 +0200 Subject: [PATCH 02/24] - fixed code to compile with CONFIG_USE_THREADS = 1 --- lib60870-C/config/lib60870_config.h | 2 +- lib60870-C/src/iec60870/cs104/cs104_connection.c | 2 ++ lib60870-C/src/iec60870/cs104/cs104_slave.c | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib60870-C/config/lib60870_config.h b/lib60870-C/config/lib60870_config.h index f7610d3f..e73f784d 100644 --- a/lib60870-C/config/lib60870_config.h +++ b/lib60870-C/config/lib60870_config.h @@ -24,7 +24,7 @@ * * CONFIG_USE_THREADS = 0 not yet supported */ -#define CONFIG_USE_THREADS 1 +#define CONFIG_USE_THREADS 0 /** * Use a separate thread to call the callback functions. This allows the user diff --git a/lib60870-C/src/iec60870/cs104/cs104_connection.c b/lib60870-C/src/iec60870/cs104/cs104_connection.c index 0ac58b0e..8e9bdfc7 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_connection.c +++ b/lib60870-C/src/iec60870/cs104/cs104_connection.c @@ -790,10 +790,12 @@ handleConnection(void* parameter) void CS104_Connection_connectAsync(CS104_Connection self) { +#if (CONFIG_USE_THREADS == 1) self->connectionHandlingThread = Thread_create(handleConnection, (void*) self, false); if (self->connectionHandlingThread) Thread_start(self->connectionHandlingThread); +#endif } bool diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 21dab00e..14997893 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -1698,6 +1698,7 @@ sendNextLowPriorityASDU(MasterConnection self) #if (CONFIG_USE_THREADS == 1) Semaphore_post(self->sentASDUsLock); #endif + return; } static bool @@ -2460,7 +2461,9 @@ CS104_Slave_startThreadless(CS104_Slave self) { if (self->isRunning == false) { +#if (CONFIG_USE_THREADS == 1) self->isThreadlessMode = true; +#endif #if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) @@ -2514,8 +2517,11 @@ CS104_Slave_isRunning(CS104_Slave self) void CS104_Slave_stop(CS104_Slave self) { +#if (CONFIG_USE_THREADS == 1) if (self->isThreadlessMode) { +#endif CS104_Slave_stopThreadless(self); +#if (CONFIG_USE_THREADS == 1) } else { if (self->isRunning) { @@ -2531,6 +2537,7 @@ CS104_Slave_stop(CS104_Slave self) self->listeningThread = NULL; } +#endif } void @@ -2571,7 +2578,9 @@ CS104_Slave_destroy(CS104_Slave self) Semaphore_post(self->openConnectionsLock); #endif +#if (CONFIG_USE_THREADS == 1) if (self->isThreadlessMode) { +#endif for (element = LinkedList_getNext(self->masterConnections); element != NULL; element = LinkedList_getNext(element)) @@ -2580,12 +2589,14 @@ CS104_Slave_destroy(CS104_Slave self) MasterConnection_destroy(connection); } +#if (CONFIG_USE_THREADS == 1) } else { /* Wait until all connections are closed */ while (CS104_Slave_getOpenConnections(self) > 0) Thread_sleep(10); } +#endif LinkedList_destroyStatic(self->masterConnections); From 0c6847e0d91d71eb81d915f997e7bb3b142fd637 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sun, 24 Jun 2018 11:44:13 +0200 Subject: [PATCH 03/24] - add support for static ASDU object allocation - changed cs104_server_no_threads example to use only static memory --- .../cs104_server_no_threads.c | 68 +++++++------------ lib60870-C/src/iec60870/cs101/cs101_asdu.c | 18 +---- lib60870-C/src/iec60870/cs101/cs101_master.c | 12 ++-- lib60870-C/src/inc/api/iec60870_common.h | 61 +++++++++++++---- .../src/inc/internal/cs101_asdu_internal.h | 20 ------ 5 files changed, 80 insertions(+), 99 deletions(-) diff --git a/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c b/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c index 795f4a80..ba6968d4 100644 --- a/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c +++ b/lib60870-C/examples/cs104_server_no_threads/cs104_server_no_threads.c @@ -17,6 +17,9 @@ sigint_handler(int signalId) running = false; } +static sCS101_StaticASDU _asdu; +static uint8_t ioBuf[250]; + void printCP56Time2a(CP56Time2a time) { @@ -53,6 +56,7 @@ clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu return true; } + static bool interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) { @@ -66,59 +70,46 @@ interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU a /* The CS101 specification only allows information objects without timestamp in GI responses */ - CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, + CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 100, -1, IEC60870_QUALITY_GOOD); - - CS101_ASDU_addInformationObject(newAsdu, io); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) - MeasuredValueScaled_create((MeasuredValueScaled) io, 101, 23, IEC60870_QUALITY_GOOD)); + MeasuredValueScaled_create((MeasuredValueScaled) &ioBuf, 100, -1, IEC60870_QUALITY_GOOD)); CS101_ASDU_addInformationObject(newAsdu, (InformationObject) - MeasuredValueScaled_create((MeasuredValueScaled) io, 102, 2300, IEC60870_QUALITY_GOOD)); + MeasuredValueScaled_create((MeasuredValueScaled) &ioBuf, 101, 23, IEC60870_QUALITY_GOOD)); - InformationObject_destroy(io); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + MeasuredValueScaled_create((MeasuredValueScaled) &ioBuf, 102, 2300, IEC60870_QUALITY_GOOD)); IMasterConnection_sendASDU(connection, newAsdu); - CS101_ASDU_destroy(newAsdu); - newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, + newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - io = (InformationObject) SinglePointInformation_create(NULL, 104, true, IEC60870_QUALITY_GOOD); - - CS101_ASDU_addInformationObject(newAsdu, io); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) - SinglePointInformation_create((SinglePointInformation) io, 105, false, IEC60870_QUALITY_GOOD)); + SinglePointInformation_create((SinglePointInformation) &ioBuf, 104, true, IEC60870_QUALITY_GOOD)); - InformationObject_destroy(io); - - IMasterConnection_sendASDU(connection, newAsdu); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + SinglePointInformation_create((SinglePointInformation) &ioBuf, 105, false, IEC60870_QUALITY_GOOD)); - CS101_ASDU_destroy(newAsdu); + IMasterConnection_sendASDU(connection, newAsdu);; - newAsdu = CS101_ASDU_create(alParams, true, CS101_COT_INTERROGATED_BY_STATION, + newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, true, CS101_COT_INTERROGATED_BY_STATION, 0, 1, false, false); - CS101_ASDU_addInformationObject(newAsdu, io = (InformationObject) SinglePointInformation_create(NULL, 300, true, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 301, false, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 302, true, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 303, false, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 304, true, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 305, false, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 306, true, IEC60870_QUALITY_GOOD)); - CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 307, false, IEC60870_QUALITY_GOOD)); - - InformationObject_destroy(io); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 300, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 301, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 302, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 303, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 304, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 305, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 306, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) &ioBuf, 307, false, IEC60870_QUALITY_GOOD)); IMasterConnection_sendASDU(connection, newAsdu); - CS101_ASDU_destroy(newAsdu); - IMasterConnection_sendACT_TERM(connection, asdu); } else { @@ -254,23 +245,18 @@ main(int argc, char** argv) nextSendTime += 1000; - CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); - - InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 110, scaledValue, IEC60870_QUALITY_GOOD); + CS101_ASDU newAsdu = CS101_ASDU_initializeStatic(&_asdu, alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); scaledValue++; - CS101_ASDU_addInformationObject(newAsdu, io); - - InformationObject_destroy(io); + CS101_ASDU_addInformationObject(newAsdu, + (InformationObject) MeasuredValueScaled_create((MeasuredValueScaled)&ioBuf, 110, scaledValue, IEC60870_QUALITY_GOOD)); /* Add ASDU to slave event queue - don't release the ASDU afterwards! * The ASDU will be released by the Slave instance when the ASDU * has been sent. */ CS104_Slave_enqueueASDU(slave, newAsdu); - - CS101_ASDU_destroy(newAsdu); } Thread_sleep(1); @@ -280,6 +266,4 @@ main(int argc, char** argv) exit_program: CS104_Slave_destroy(slave); - - Thread_sleep(500); } diff --git a/lib60870-C/src/iec60870/cs101/cs101_asdu.c b/lib60870-C/src/iec60870/cs101/cs101_asdu.c index f1ab004c..217ebbb9 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_asdu.c +++ b/lib60870-C/src/iec60870/cs101/cs101_asdu.c @@ -85,8 +85,7 @@ CS101_ASDU CS101_ASDU_create(CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, bool isTest, bool isNegative) { - CS101_StaticASDU self = (CS101_StaticASDU) GLOBAL_MALLOC(sizeof(struct sCS101_StaticASDU)); - //TODO support allocation from static pool + CS101_StaticASDU self = (CS101_StaticASDU) GLOBAL_MALLOC(sizeof(sCS101_StaticASDU)); if (self != NULL) CS101_ASDU_initializeStatic(self, parameters, isSequence, cot, oa, ca, isTest, isNegative); @@ -98,8 +97,6 @@ CS101_ASDU CS101_ASDU_initializeStatic(CS101_StaticASDU self, CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, bool isTest, bool isNegative) { - self->stackCreated = false; - int asduHeaderLength = 2 + parameters->sizeOfCOT + parameters->sizeOfCA; self->encodedData[0] = (uint8_t) 0; @@ -146,12 +143,6 @@ CS101_ASDU_destroy(CS101_ASDU self) GLOBAL_FREEMEM(self); } -bool -CS101_ASDU_isStackCreated(CS101_ASDU self) -{ - return self->stackCreated; -} - void CS101_ASDU_encode(CS101_ASDU self, Frame frame) { @@ -167,11 +158,8 @@ CS101_ASDU_createFromBuffer(CS101_AppLayerParameters parameters, uint8_t* msg, i return NULL; CS101_ASDU self = (CS101_ASDU) GLOBAL_MALLOC(sizeof(struct sCS101_ASDU)); - //TODO support allocation from static pool if (self != NULL) { - - self->stackCreated = true; self->parameters = parameters; self->asdu = msg; @@ -214,9 +202,7 @@ CS101_ASDU_addInformationObject(CS101_ASDU self, InformationObject io) int numberOfElements = CS101_ASDU_getNumberOfElements(self); if (numberOfElements == 0) { - if (self->stackCreated == false) { - ((CS101_StaticASDU)self)->encodedData[0] = (uint8_t) InformationObject_getType(io); - } + ((CS101_StaticASDU)self)->encodedData[0] = (uint8_t) InformationObject_getType(io); encoded = InformationObject_encode(io, (Frame) &asduFrame, self->parameters, false); } diff --git a/lib60870-C/src/iec60870/cs101/cs101_master.c b/lib60870-C/src/iec60870/cs101/cs101_master.c index 82bed9f9..2f723dab 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_master.c +++ b/lib60870-C/src/iec60870/cs101/cs101_master.c @@ -349,7 +349,7 @@ CS101_Master_sendLinkLayerTestFunction(CS101_Master self) void CS101_Master_sendInterrogationCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, QualifierOfInterrogation qoi) { - struct sCS101_StaticASDU _asdu; + sCS101_StaticASDU _asdu; CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, cot, self->alParameters.originatorAddress, ca, false, false); @@ -365,7 +365,7 @@ CS101_Master_sendInterrogationCommand(CS101_Master self, CS101_CauseOfTransmissi void CS101_Master_sendCounterInterrogationCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, uint8_t qcc) { - struct sCS101_StaticASDU _asdu; + sCS101_StaticASDU _asdu; CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, cot, self->alParameters.originatorAddress, ca, false, false); @@ -381,7 +381,7 @@ CS101_Master_sendCounterInterrogationCommand(CS101_Master self, CS101_CauseOfTra void CS101_Master_sendReadCommand(CS101_Master self, int ca, int ioa) { - struct sCS101_StaticASDU _asdu; + sCS101_StaticASDU _asdu; CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, CS101_COT_REQUEST, self->alParameters.originatorAddress, ca, false, false); @@ -397,7 +397,7 @@ CS101_Master_sendReadCommand(CS101_Master self, int ca, int ioa) void CS101_Master_sendClockSyncCommand(CS101_Master self, int ca, CP56Time2a time) { - struct sCS101_StaticASDU _asdu; + sCS101_StaticASDU _asdu; CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, CS101_COT_ACTIVATION, self->alParameters.originatorAddress, ca, false, false); @@ -413,7 +413,7 @@ CS101_Master_sendClockSyncCommand(CS101_Master self, int ca, CP56Time2a time) void CS101_Master_sendTestCommand(CS101_Master self, int ca) { - struct sCS101_StaticASDU _asdu; + sCS101_StaticASDU _asdu; CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, CS101_COT_ACTIVATION, self->alParameters.originatorAddress, ca, false, false); @@ -429,7 +429,7 @@ CS101_Master_sendTestCommand(CS101_Master self, int ca) void CS101_Master_sendProcessCommand(CS101_Master self, CS101_CauseOfTransmission cot, int ca, InformationObject command) { - struct sCS101_StaticASDU _asdu; + sCS101_StaticASDU _asdu; CS101_ASDU asdu = CS101_ASDU_initializeStatic(&_asdu, &(self->alParameters), false, cot, self->alParameters.originatorAddress, ca, false, false); diff --git a/lib60870-C/src/inc/api/iec60870_common.h b/lib60870-C/src/inc/api/iec60870_common.h index 1fc11c9b..24a22c75 100644 --- a/lib60870-C/src/inc/api/iec60870_common.h +++ b/lib60870-C/src/inc/api/iec60870_common.h @@ -104,11 +104,37 @@ typedef void (*IEC60870_LinkLayerStateChangedHandler) (void* parameter, int addr */ typedef void (*IEC60870_RawMessageHandler) (void* parameter, uint8_t* msg, int msgSize, bool sent); +/** + * \brief Parameters for the CS101/CS104 application layer + */ +typedef struct sCS101_AppLayerParameters* CS101_AppLayerParameters; + +struct sCS101_AppLayerParameters { + int sizeOfTypeId; /* size of the type id (default = 1 - don't change) */ + int sizeOfVSQ; /* don't change */ + int sizeOfCOT; /* size of COT (1/2 - default = 2 -> COT includes OA) */ + int originatorAddress; /* originator address (OA) to use (0-255) */ + int sizeOfCA; /* size of common address (CA) of ASDU (1/2 - default = 2) */ + int sizeOfIOA; /* size of information object address (IOA) (1/2/3 - default = 3) */ + int maxSizeOfASDU; /* maximum size of the ASDU that is generated - the maximum maximum value is 249 for IEC 104 and 254 for IEC 101 */ +}; + /** * \brief Application Service Data Unit (ASDU) for the CS101/CS104 application layer */ typedef struct sCS101_ASDU* CS101_ASDU; +typedef struct { + CS101_AppLayerParameters parameters; + uint8_t* asdu; + int asduHeaderLength; + uint8_t* payload; + int payloadSize; + uint8_t encodedData[256]; +} sCS101_StaticASDU; + +typedef sCS101_StaticASDU* CS101_StaticASDU; + typedef struct sCP16Time2a* CP16Time2a; struct sCP16Time2a { @@ -148,21 +174,6 @@ struct sBinaryCounterReading { uint8_t encodedValue[5]; }; -/** - * \brief Parameters for the CS101/CS104 application layer - */ -typedef struct sCS101_AppLayerParameters* CS101_AppLayerParameters; - -struct sCS101_AppLayerParameters { - int sizeOfTypeId; /* size of the type id (default = 1 - don't change) */ - int sizeOfVSQ; /* don't change */ - int sizeOfCOT; /* size of COT (1/2 - default = 2 -> COT includes OA) */ - int originatorAddress; /* originator address (OA) to use (0-255) */ - int sizeOfCA; /* size of common address (CA) of ASDU (1/2 - default = 2) */ - int sizeOfIOA; /* size of information object address (IOA) (1/2/3 - default = 3) */ - int maxSizeOfASDU; /* maximum size of the ASDU that is generated - the maximum maximum value is 249 for IEC 104 and 254 for IEC 101 */ -}; - /** * \brief Parameters for CS104 connections - APCI (application protocol control information) */ @@ -416,6 +427,26 @@ CS101_ASDU CS101_ASDU_create(CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, bool isTest, bool isNegative); +/** + * \brief Create a new ASDU and store it in the provided static ASDU structure. + * + * NOTE: The type ID will be derived from the first InformationObject that will be added. + * + * \param self pointer to the statically allocated data structure + * \param parameters the application layer parameters used to encode the ASDU + * \param isSequence if the information objects will be encoded as a compact sequence of information objects with subsequent IOA values + * \param cot cause of transmission (COT) + * \param oa originator address (OA) to be used + * \param ca the common address (CA) of the ASDU + * \param isTest if the test flag will be set or not + * \param isNegative if the negative falg will be set or not + * + * \return the new CS101_ASDU instance + */ +CS101_ASDU +CS101_ASDU_initializeStatic(CS101_StaticASDU self, CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, + bool isTest, bool isNegative); + /** * \brief Destroy the ASDU object (release all resources) */ diff --git a/lib60870-C/src/inc/internal/cs101_asdu_internal.h b/lib60870-C/src/inc/internal/cs101_asdu_internal.h index cf6a123c..da414080 100644 --- a/lib60870-C/src/inc/internal/cs101_asdu_internal.h +++ b/lib60870-C/src/inc/internal/cs101_asdu_internal.h @@ -33,7 +33,6 @@ #include "lib60870_internal.h" struct sCS101_ASDU { - bool stackCreated; CS101_AppLayerParameters parameters; uint8_t* asdu; int asduHeaderLength; @@ -41,18 +40,6 @@ struct sCS101_ASDU { int payloadSize; }; -typedef struct sCS101_StaticASDU* CS101_StaticASDU; - -struct sCS101_StaticASDU { - bool stackCreated; - CS101_AppLayerParameters parameters; - uint8_t* asdu; - int asduHeaderLength; - uint8_t* payload; - int payloadSize; - uint8_t encodedData[256]; -}; - /** * \brief create a new (read-only) instance * @@ -61,11 +48,4 @@ struct sCS101_StaticASDU { CS101_ASDU CS101_ASDU_createFromBuffer(CS101_AppLayerParameters parameters, uint8_t* msg, int msgLength); -CS101_ASDU -CS101_ASDU_initializeStatic(CS101_StaticASDU self, CS101_AppLayerParameters parameters, bool isSequence, CS101_CauseOfTransmission cot, int oa, int ca, - bool isTest, bool isNegative); - -bool -CS101_ASDU_isStackCreated(CS101_ASDU self); - #endif /* SRC_INC_INTERNAL_CS101_ASDU_INTERNAL_H_ */ From 14df0bba155bbb3bf5a9e6c461d05c79b5ea8891 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 28 Jun 2018 15:07:31 +0200 Subject: [PATCH 04/24] - CS104 slave: added automatic C_CS ACTCON response for clock sync handler --- lib60870-C/src/iec60870/cs101/cs101_asdu.c | 7 ++++++ lib60870-C/src/iec60870/cs104/cs104_slave.c | 26 +++++++++++++++++++-- lib60870-C/src/inc/api/iec60870_common.h | 8 +++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/lib60870-C/src/iec60870/cs101/cs101_asdu.c b/lib60870-C/src/iec60870/cs101/cs101_asdu.c index 217ebbb9..859c72b8 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_asdu.c +++ b/lib60870-C/src/iec60870/cs101/cs101_asdu.c @@ -227,6 +227,13 @@ CS101_ASDU_addInformationObject(CS101_ASDU self, InformationObject io) return encoded; } +void +CS101_ASDU_removeAllElements(CS101_ASDU self) +{ + self->asdu[1] = (self->asdu[1] & 0x80); + self->payloadSize = 0; +} + bool CS101_ASDU_isTest(CS101_ASDU self) { diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 14997893..03db20ea 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -1333,10 +1333,32 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElement(asdu, 0); if (slave->clockSyncHandler(slave->clockSyncHandlerParameter, - &(self->iMasterConnection), asdu, ClockSynchronizationCommand_getTime(csc))) - messageHandled = true; + &(self->iMasterConnection), asdu, ClockSynchronizationCommand_getTime(csc))) { + + CS101_ASDU_removeAllElements(asdu); + + struct sCP56Time2a timeValue; + + CP56Time2a_createFromMsTimestamp(&timeValue, Hal_getTimeInMs()); + + ClockSynchronizationCommand_create(csc, 0, &timeValue); + + CS101_ASDU_addInformationObject(asdu, (InformationObject) csc); + + CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); + + CS104_Slave_enqueueASDU(slave, asdu); + } + else { + CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); + CS101_ASDU_setNegative(asdu, true); + + sendASDUInternal(self, asdu); + } ClockSynchronizationCommand_destroy(csc); + + messageHandled = true; } } else diff --git a/lib60870-C/src/inc/api/iec60870_common.h b/lib60870-C/src/inc/api/iec60870_common.h index 24a22c75..0a28ba1c 100644 --- a/lib60870-C/src/inc/api/iec60870_common.h +++ b/lib60870-C/src/inc/api/iec60870_common.h @@ -464,6 +464,14 @@ CS101_ASDU_destroy(CS101_ASDU self); bool CS101_ASDU_addInformationObject(CS101_ASDU self, InformationObject io); +/** + * \brief remove all information elements from the ASDU object + * + * \param self ASDU object instance + */ +void +CS101_ASDU_removeAllElements(CS101_ASDU self); + /** * \brief Get the elapsed time in ms */ From ca6157c38c460f4cf01fc70287004e8f92b37d7a Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 28 Jun 2018 16:26:32 +0200 Subject: [PATCH 05/24] - CS104 slave: time sync handler can return time to be used for C_CS ACT-CON message --- lib60870-C/examples/cs104_server/simple_server.c | 7 +++++++ lib60870-C/src/iec60870/cs104/cs104_slave.c | 10 ++++------ lib60870-C/src/inc/api/iec60870_slave.h | 12 +++++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/lib60870-C/examples/cs104_server/simple_server.c b/lib60870-C/examples/cs104_server/simple_server.c index 03fe08e1..2d116407 100644 --- a/lib60870-C/examples/cs104_server/simple_server.c +++ b/lib60870-C/examples/cs104_server/simple_server.c @@ -50,6 +50,13 @@ clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu { printf("Process time sync command with time "); printCP56Time2a(newTime); printf("\n"); + uint64_t newSystemTimeInMs = CP56Time2a_toMsTimestamp(newTime); + + /* Set time for ACT_CON message */ + CP56Time2a_setFromMsTimestamp(newTime, Hal_getTimeInMs()); + + /* update system time here */ + return true; } diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 03db20ea..251bb87a 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -1332,16 +1332,14 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElement(asdu, 0); + CP56Time2a newTime = ClockSynchronizationCommand_getTime(csc); + if (slave->clockSyncHandler(slave->clockSyncHandlerParameter, - &(self->iMasterConnection), asdu, ClockSynchronizationCommand_getTime(csc))) { + &(self->iMasterConnection), asdu, newTime)) { CS101_ASDU_removeAllElements(asdu); - struct sCP56Time2a timeValue; - - CP56Time2a_createFromMsTimestamp(&timeValue, Hal_getTimeInMs()); - - ClockSynchronizationCommand_create(csc, 0, &timeValue); + ClockSynchronizationCommand_create(csc, 0, newTime); CS101_ASDU_addInformationObject(asdu, (InformationObject) csc); diff --git a/lib60870-C/src/inc/api/iec60870_slave.h b/lib60870-C/src/inc/api/iec60870_slave.h index 4169fbf6..7cf51ca1 100644 --- a/lib60870-C/src/inc/api/iec60870_slave.h +++ b/lib60870-C/src/inc/api/iec60870_slave.h @@ -1,5 +1,5 @@ /* - * Copyright 2016, 2017 MZ Automation GmbH + * Copyright 2016-2018 MZ Automation GmbH * * This file is part of lib60870-C * @@ -147,6 +147,16 @@ typedef bool (*CS101_ReadHandler) (void* parameter, IMasterConnection connection /** * \brief Handler for clock synchronization command (C_CS_NA_1 - 103) + * + * This handler will be called whenever a time synchronization command is received. + * NOTE: The \ref CS104_Slave instance will automatically send an ACT-CON message for the received time sync command. + * + * \param[in] parameter user provided parameter + * \param[in] connection represents the (TCP) connection that received the time sync command + * \param[in] asdu the received ASDU + * \param[in,out] the time received with the time sync message. The user can update this time for the ACT-CON message + * + * \return true when time synchronization has been successful, false otherwise */ typedef bool (*CS101_ClockSynchronizationHandler) (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime); From 7656c365f2920c1f92e221859bb2bc5a0bf6f636 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 28 Jun 2018 16:32:14 +0200 Subject: [PATCH 06/24] - updated version number to 2.1.0 --- lib60870-C/CMakeLists.txt | 4 ++-- lib60870-C/src/inc/api/iec60870_common.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib60870-C/CMakeLists.txt b/lib60870-C/CMakeLists.txt index a7f5f5d1..d7802777 100644 --- a/lib60870-C/CMakeLists.txt +++ b/lib60870-C/CMakeLists.txt @@ -11,8 +11,8 @@ project(lib60870-C) ENABLE_TESTING() set(LIB_VERSION_MAJOR "2") -set(LIB_VERSION_MINOR "0") -set(LIB_VERSION_PATCH "2") +set(LIB_VERSION_MINOR "1") +set(LIB_VERSION_PATCH "0") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/third_party/cmake/modules/") diff --git a/lib60870-C/src/inc/api/iec60870_common.h b/lib60870-C/src/inc/api/iec60870_common.h index 0a28ba1c..a3cd6e44 100644 --- a/lib60870-C/src/inc/api/iec60870_common.h +++ b/lib60870-C/src/inc/api/iec60870_common.h @@ -46,7 +46,7 @@ extern "C" { #define IEC_60870_5_104_DEFAULT_TLS_PORT 19998 #define LIB60870_VERSION_MAJOR 2 -#define LIB60870_VERSION_MINOR 0 +#define LIB60870_VERSION_MINOR 1 #define LIB60870_VERSION_PATCH 0 /** From 76a1f69118fbb754be4c5ec288b3bcfd799a1a7d Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 28 Jun 2018 17:54:00 +0200 Subject: [PATCH 07/24] - added CS101_ASDU_getElementEx function to avoid dynamic memory allocation --- lib60870-C/src/iec60870/cs101/cs101_asdu.c | 202 ++++++++++---------- lib60870-C/src/iec60870/cs101/cs101_slave.c | 36 ++-- lib60870-C/src/iec60870/cs104/cs104_slave.c | 36 ++-- lib60870-C/src/inc/api/iec60870_common.h | 11 ++ 4 files changed, 156 insertions(+), 129 deletions(-) diff --git a/lib60870-C/src/iec60870/cs101/cs101_asdu.c b/lib60870-C/src/iec60870/cs101/cs101_asdu.c index 859c72b8..f86412a5 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_asdu.c +++ b/lib60870-C/src/iec60870/cs101/cs101_asdu.c @@ -360,6 +360,12 @@ CS101_ASDU_getNumberOfElements(CS101_ASDU self) InformationObject CS101_ASDU_getElement(CS101_ASDU self, int index) +{ + return CS101_ASDU_getElementEx(self, NULL, index); +} + +InformationObject +CS101_ASDU_getElementEx(CS101_ASDU self, InformationObject io, int index) { InformationObject retVal = NULL; @@ -372,13 +378,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 1; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) SinglePointInformation_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) SinglePointInformation_getFromBuffer((SinglePointInformation) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) SinglePointInformation_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) SinglePointInformation_getFromBuffer((SinglePointInformation) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -388,13 +394,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 4; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) SinglePointWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) SinglePointWithCP24Time2a_getFromBuffer((SinglePointWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) SinglePointWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) SinglePointWithCP24Time2a_getFromBuffer((SinglePointWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -404,13 +410,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 1; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) DoublePointInformation_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) DoublePointInformation_getFromBuffer((DoublePointInformation) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) DoublePointInformation_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) DoublePointInformation_getFromBuffer((DoublePointInformation) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); @@ -421,13 +427,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 4; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) DoublePointWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) DoublePointWithCP24Time2a_getFromBuffer((DoublePointWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) DoublePointWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) DoublePointWithCP24Time2a_getFromBuffer((DoublePointWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -437,13 +443,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 2; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) StepPositionInformation_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) StepPositionInformation_getFromBuffer((StepPositionInformation) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) StepPositionInformation_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) StepPositionInformation_getFromBuffer((StepPositionInformation) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -453,13 +459,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 5; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) StepPositionWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) StepPositionWithCP24Time2a_getFromBuffer((StepPositionWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) StepPositionWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) StepPositionWithCP24Time2a_getFromBuffer((StepPositionWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -469,13 +475,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 5; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) BitString32_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) BitString32_getFromBuffer((BitString32) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) BitString32_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) BitString32_getFromBuffer((BitString32) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -485,13 +491,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 8; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) Bitstring32WithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) Bitstring32WithCP24Time2a_getFromBuffer((Bitstring32WithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) Bitstring32WithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) Bitstring32WithCP24Time2a_getFromBuffer((Bitstring32WithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -501,13 +507,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 3; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueNormalized_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalized_getFromBuffer((MeasuredValueNormalized) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueNormalized_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalized_getFromBuffer((MeasuredValueNormalized) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -517,13 +523,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 6; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueNormalizedWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalizedWithCP24Time2a_getFromBuffer((MeasuredValueNormalizedWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueNormalizedWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalizedWithCP24Time2a_getFromBuffer((MeasuredValueNormalizedWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -533,13 +539,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 3; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueScaled_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueScaled_getFromBuffer((MeasuredValueScaled) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueScaled_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueScaled_getFromBuffer((MeasuredValueScaled) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -549,13 +555,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 6; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueScaledWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueScaledWithCP24Time2a_getFromBuffer((MeasuredValueScaledWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueScaledWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueScaledWithCP24Time2a_getFromBuffer((MeasuredValueScaledWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -566,13 +572,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 5; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueShort_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueShort_getFromBuffer((MeasuredValueShort) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueShort_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueShort_getFromBuffer((MeasuredValueShort) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); @@ -583,13 +589,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 8; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueShortWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueShortWithCP24Time2a_getFromBuffer((MeasuredValueShortWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueShortWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueShortWithCP24Time2a_getFromBuffer((MeasuredValueShortWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -599,13 +605,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 5; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) IntegratedTotals_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) IntegratedTotals_getFromBuffer((IntegratedTotals) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) IntegratedTotals_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) IntegratedTotals_getFromBuffer((IntegratedTotals) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -615,13 +621,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 8; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) IntegratedTotalsWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) IntegratedTotalsWithCP24Time2a_getFromBuffer((IntegratedTotalsWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) IntegratedTotalsWithCP24Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) IntegratedTotalsWithCP24Time2a_getFromBuffer((IntegratedTotalsWithCP24Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -631,13 +637,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 6; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) EventOfProtectionEquipment_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) EventOfProtectionEquipment_getFromBuffer((EventOfProtectionEquipment) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) EventOfProtectionEquipment_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) EventOfProtectionEquipment_getFromBuffer((EventOfProtectionEquipment) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -647,13 +653,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 7; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) PackedStartEventsOfProtectionEquipment_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedStartEventsOfProtectionEquipment_getFromBuffer((PackedStartEventsOfProtectionEquipment) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) PackedStartEventsOfProtectionEquipment_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedStartEventsOfProtectionEquipment_getFromBuffer((PackedStartEventsOfProtectionEquipment) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -663,13 +669,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 7; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) PackedOutputCircuitInfo_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedOutputCircuitInfo_getFromBuffer((PackedOutputCircuitInfo) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) PackedOutputCircuitInfo_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedOutputCircuitInfo_getFromBuffer((PackedOutputCircuitInfo) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -679,13 +685,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 5; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) PackedSinglePointWithSCD_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedSinglePointWithSCD_getFromBuffer((PackedSinglePointWithSCD) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) PackedSinglePointWithSCD_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedSinglePointWithSCD_getFromBuffer((PackedSinglePointWithSCD) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -695,13 +701,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 2; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueNormalizedWithoutQuality_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalizedWithoutQuality_getFromBuffer((MeasuredValueNormalizedWithoutQuality) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueNormalizedWithoutQuality_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalizedWithoutQuality_getFromBuffer((MeasuredValueNormalizedWithoutQuality) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -711,13 +717,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 8; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) SinglePointWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) SinglePointWithCP56Time2a_getFromBuffer((SinglePointWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) SinglePointWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) SinglePointWithCP56Time2a_getFromBuffer((SinglePointWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -727,13 +733,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 8; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) DoublePointWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) DoublePointWithCP56Time2a_getFromBuffer((DoublePointWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) DoublePointWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) DoublePointWithCP56Time2a_getFromBuffer((DoublePointWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -743,13 +749,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 9; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) StepPositionWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) StepPositionWithCP56Time2a_getFromBuffer((StepPositionWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) StepPositionWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) StepPositionWithCP56Time2a_getFromBuffer((StepPositionWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -759,13 +765,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 12; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) Bitstring32WithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) Bitstring32WithCP56Time2a_getFromBuffer((Bitstring32WithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) Bitstring32WithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) Bitstring32WithCP56Time2a_getFromBuffer((Bitstring32WithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -775,13 +781,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 10; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueNormalizedWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalizedWithCP56Time2a_getFromBuffer((MeasuredValueNormalizedWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueNormalizedWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueNormalizedWithCP56Time2a_getFromBuffer((MeasuredValueNormalizedWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -791,13 +797,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 10; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueScaledWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueScaledWithCP56Time2a_getFromBuffer((MeasuredValueScaledWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueScaledWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueScaledWithCP56Time2a_getFromBuffer((MeasuredValueScaledWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -807,13 +813,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 12; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) MeasuredValueShortWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueShortWithCP56Time2a_getFromBuffer((MeasuredValueShortWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) MeasuredValueShortWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) MeasuredValueShortWithCP56Time2a_getFromBuffer((MeasuredValueShortWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -823,13 +829,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 12; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) IntegratedTotalsWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) IntegratedTotalsWithCP56Time2a_getFromBuffer((IntegratedTotalsWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) IntegratedTotalsWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) IntegratedTotalsWithCP56Time2a_getFromBuffer((IntegratedTotalsWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -839,13 +845,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 10; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) EventOfProtectionEquipmentWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) EventOfProtectionEquipmentWithCP56Time2a_getFromBuffer((EventOfProtectionEquipmentWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) EventOfProtectionEquipmentWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) EventOfProtectionEquipmentWithCP56Time2a_getFromBuffer((EventOfProtectionEquipmentWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -855,13 +861,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 11; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) PackedStartEventsOfProtectionEquipmentWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedStartEventsOfProtectionEquipmentWithCP56Time2a_getFromBuffer((PackedStartEventsOfProtectionEquipmentWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) PackedStartEventsOfProtectionEquipmentWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedStartEventsOfProtectionEquipmentWithCP56Time2a_getFromBuffer((PackedStartEventsOfProtectionEquipmentWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -871,13 +877,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 11; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) PackedOutputCircuitInfoWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedOutputCircuitInfoWithCP56Time2a_getFromBuffer((PackedOutputCircuitInfoWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) PackedOutputCircuitInfoWithCP56Time2a_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) PackedOutputCircuitInfoWithCP56Time2a_getFromBuffer((PackedOutputCircuitInfoWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; @@ -888,7 +894,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 1; - retVal = (InformationObject) SingleCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SingleCommand_getFromBuffer((SingleCommand) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -897,7 +903,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 1; - retVal = (InformationObject) DoubleCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) DoubleCommand_getFromBuffer((DoubleCommand) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -905,7 +911,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 1; - retVal = (InformationObject) StepCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) StepCommand_getFromBuffer((StepCommand) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -913,7 +919,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 3; - retVal = (InformationObject) SetpointCommandNormalized_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SetpointCommandNormalized_getFromBuffer((SetpointCommandNormalized) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -922,7 +928,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 3; - retVal = (InformationObject) SetpointCommandScaled_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SetpointCommandScaled_getFromBuffer((SetpointCommandScaled) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -930,7 +936,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 5; - retVal = (InformationObject) SetpointCommandShort_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SetpointCommandShort_getFromBuffer((SetpointCommandShort) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -938,7 +944,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 4; - retVal = (InformationObject) Bitstring32Command_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) Bitstring32Command_getFromBuffer((Bitstring32Command) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -948,7 +954,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 8; - retVal = (InformationObject) SingleCommandWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SingleCommandWithCP56Time2a_getFromBuffer((SingleCommandWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -956,7 +962,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 8; - retVal = (InformationObject) DoubleCommandWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) DoubleCommandWithCP56Time2a_getFromBuffer((DoubleCommandWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -964,7 +970,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 8; - retVal = (InformationObject) StepCommandWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) StepCommandWithCP56Time2a_getFromBuffer((StepCommandWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -972,7 +978,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 10; - retVal = (InformationObject) SetpointCommandNormalizedWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SetpointCommandNormalizedWithCP56Time2a_getFromBuffer((SetpointCommandNormalizedWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -980,7 +986,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 10; - retVal = (InformationObject) SetpointCommandScaledWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SetpointCommandScaledWithCP56Time2a_getFromBuffer((SetpointCommandScaledWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -988,7 +994,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 12; - retVal = (InformationObject) SetpointCommandShortWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) SetpointCommandShortWithCP56Time2a_getFromBuffer((SetpointCommandShortWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -996,7 +1002,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 11; - retVal = (InformationObject) Bitstring32CommandWithCP56Time2a_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) Bitstring32CommandWithCP56Time2a_getFromBuffer((Bitstring32CommandWithCP56Time2a) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -1004,49 +1010,49 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 1; - retVal = (InformationObject) EndOfInitialization_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) EndOfInitialization_getFromBuffer((EndOfInitialization) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_IC_NA_1: /* 100 - Interrogation command */ - retVal = (InformationObject) InterrogationCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) InterrogationCommand_getFromBuffer((InterrogationCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_CI_NA_1: /* 101 - Counter interrogation command */ - retVal = (InformationObject) CounterInterrogationCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) CounterInterrogationCommand_getFromBuffer((CounterInterrogationCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_RD_NA_1: /* 102 - Read command */ - retVal = (InformationObject) ReadCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) ReadCommand_getFromBuffer((ReadCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_CS_NA_1: /* 103 - Clock synchronization command */ - retVal = (InformationObject) ClockSynchronizationCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) ClockSynchronizationCommand_getFromBuffer((ClockSynchronizationCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_TS_NA_1: /* 104 - Test command */ - retVal = (InformationObject) TestCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) TestCommand_getFromBuffer((TestCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_RP_NA_1: /* 105 - Reset process command */ - retVal = (InformationObject) ResetProcessCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) ResetProcessCommand_getFromBuffer((ResetProcessCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; case C_CD_NA_1: /* 106 - Delay acquisition command */ - retVal = (InformationObject) DelayAcquisitionCommand_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) DelayAcquisitionCommand_getFromBuffer((DelayAcquisitionCommand) io, self->parameters, self->payload, self->payloadSize, 0); break; @@ -1054,7 +1060,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 3; - retVal = (InformationObject) ParameterNormalizedValue_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) ParameterNormalizedValue_getFromBuffer((ParameterNormalizedValue) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -1062,7 +1068,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 3; - retVal = (InformationObject) ParameterScaledValue_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) ParameterScaledValue_getFromBuffer((ParameterScaledValue) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -1070,7 +1076,7 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 5; - retVal = (InformationObject) ParameterFloatValue_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) ParameterFloatValue_getFromBuffer((ParameterFloatValue) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; @@ -1078,43 +1084,43 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = self->parameters->sizeOfIOA + 1; - retVal = (InformationObject) ParameterActivation_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, index * elementSize); + retVal = (InformationObject) ParameterActivation_getFromBuffer((ParameterActivation) io, self->parameters, self->payload, self->payloadSize, index * elementSize); break; case F_FR_NA_1: /* 120 - File ready */ - retVal = (InformationObject) FileReady_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) FileReady_getFromBuffer((FileReady) io, self->parameters, self->payload, self->payloadSize, 0); break; case F_SR_NA_1: /* 121 - Section ready */ - retVal = (InformationObject) SectionReady_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) SectionReady_getFromBuffer((SectionReady) io, self->parameters, self->payload, self->payloadSize, 0); break; case F_SC_NA_1: /* 122 - Call/Select directory/file/section */ - retVal = (InformationObject) FileCallOrSelect_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) FileCallOrSelect_getFromBuffer((FileCallOrSelect) io, self->parameters, self->payload, self->payloadSize, 0); break; case F_LS_NA_1: /* 123 - Last segment/section */ - retVal = (InformationObject) FileLastSegmentOrSection_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) FileLastSegmentOrSection_getFromBuffer((FileLastSegmentOrSection) io, self->parameters, self->payload, self->payloadSize, 0); break; case F_AF_NA_1: /* 124 - ACK file/section */ - retVal = (InformationObject) FileACK_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) FileACK_getFromBuffer((FileACK) io, self->parameters, self->payload, self->payloadSize, 0); break; case F_SG_NA_1: /* 125 - File segment */ - retVal = (InformationObject) FileSegment_getFromBuffer(NULL, self->parameters, self->payload, self->payloadSize, 0); + retVal = (InformationObject) FileSegment_getFromBuffer((FileSegment) io, self->parameters, self->payload, self->payloadSize, 0); break; @@ -1123,13 +1129,13 @@ CS101_ASDU_getElement(CS101_ASDU self, int index) elementSize = 13; if (CS101_ASDU_isSequence(self)) { - retVal = (InformationObject) FileDirectory_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) FileDirectory_getFromBuffer((FileDirectory) io, self->parameters, self->payload, self->payloadSize, self->parameters->sizeOfIOA + (index * elementSize), true); InformationObject_setObjectAddress(retVal, InformationObject_ParseObjectAddress(self->parameters, self->payload, 0) + index); } else - retVal = (InformationObject) FileDirectory_getFromBuffer(NULL, self->parameters, + retVal = (InformationObject) FileDirectory_getFromBuffer((FileDirectory) io, self->parameters, self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; diff --git a/lib60870-C/src/iec60870/cs101/cs101_slave.c b/lib60870-C/src/iec60870/cs101/cs101_slave.c index e0653fa4..44c80824 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_slave.c +++ b/lib60870-C/src/iec60870/cs101/cs101_slave.c @@ -579,13 +579,13 @@ handleASDU(CS101_Slave self, CS101_ASDU asdu) if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_DEACTIVATION)) { if (self->interrogationHandler != NULL) { - InterrogationCommand irc = (InterrogationCommand) CS101_ASDU_getElement(asdu, 0); + union uInformationObject _io; + + InterrogationCommand irc = (InterrogationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (self->interrogationHandler(self->interrogationHandlerParameter, &(self->iMasterConnection), asdu, InterrogationCommand_getQOI(irc))) messageHandled = true; - - InterrogationCommand_destroy(irc); } } else @@ -601,14 +601,13 @@ handleASDU(CS101_Slave self, CS101_ASDU asdu) if (self->counterInterrogationHandler != NULL) { - CounterInterrogationCommand cic = (CounterInterrogationCommand) CS101_ASDU_getElement(asdu, 0); + union uInformationObject _io; + CounterInterrogationCommand cic = (CounterInterrogationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (self->counterInterrogationHandler(self->counterInterrogationHandlerParameter, &(self->iMasterConnection), asdu, CounterInterrogationCommand_getQCC(cic))) messageHandled = true; - - CounterInterrogationCommand_destroy(cic); } } else @@ -622,13 +621,14 @@ handleASDU(CS101_Slave self, CS101_ASDU asdu) if (cot == CS101_COT_REQUEST) { if (self->readHandler != NULL) { - ReadCommand rc = (ReadCommand) CS101_ASDU_getElement(asdu, 0); + + union uInformationObject _io; + + ReadCommand rc = (ReadCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (self->readHandler(self->readHandlerParameter, &(self->iMasterConnection), asdu, InformationObject_getObjectAddress((InformationObject) rc))) messageHandled = true; - - ReadCommand_destroy(rc); } } else @@ -644,13 +644,15 @@ handleASDU(CS101_Slave self, CS101_ASDU asdu) if (self->clockSyncHandler != NULL) { - ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElement(asdu, 0); + union uInformationObject _io; + + ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (self->clockSyncHandler(self->clockSyncHandlerParameter, &(self->iMasterConnection), asdu, ClockSynchronizationCommand_getTime(csc))) messageHandled = true; - ClockSynchronizationCommand_destroy(csc); + //TODO send ACT-CON message } } else @@ -680,13 +682,14 @@ handleASDU(CS101_Slave self, CS101_ASDU asdu) if (cot == CS101_COT_ACTIVATION) { if (self->resetProcessHandler != NULL) { - ResetProcessCommand rpc = (ResetProcessCommand) CS101_ASDU_getElement(asdu, 0); + + union uInformationObject _io; + + ResetProcessCommand rpc = (ResetProcessCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (self->resetProcessHandler(self->resetProcessHandlerParameter, &(self->iMasterConnection), asdu, ResetProcessCommand_getQRP(rpc))) messageHandled = true; - - ResetProcessCommand_destroy(rpc); } } @@ -702,7 +705,10 @@ handleASDU(CS101_Slave self, CS101_ASDU asdu) if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_SPONTANEOUS)) { if (self->delayAcquisitionHandler != NULL) { - DelayAcquisitionCommand dac = (DelayAcquisitionCommand) CS101_ASDU_getElement(asdu, 0); + + union uInformationObject _io; + + DelayAcquisitionCommand dac = (DelayAcquisitionCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (self->delayAcquisitionHandler(self->delayAcquisitionHandlerParameter, &(self->iMasterConnection), asdu, DelayAcquisitionCommand_getDelay(dac))) diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 251bb87a..be4bb010 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -1265,13 +1265,13 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_DEACTIVATION)) { if (slave->interrogationHandler != NULL) { - InterrogationCommand irc = (InterrogationCommand) CS101_ASDU_getElement(asdu, 0); + union uInformationObject _io; + + InterrogationCommand irc = (InterrogationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (slave->interrogationHandler(slave->interrogationHandlerParameter, &(self->iMasterConnection), asdu, InterrogationCommand_getQOI(irc))) messageHandled = true; - - InterrogationCommand_destroy(irc); } } else @@ -1287,8 +1287,9 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) if (slave->counterInterrogationHandler != NULL) { - CounterInterrogationCommand cic = (CounterInterrogationCommand) CS101_ASDU_getElement(asdu, 0); + union uInformationObject _io; + CounterInterrogationCommand cic = (CounterInterrogationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (slave->counterInterrogationHandler(slave->counterInterrogationHandlerParameter, &(self->iMasterConnection), asdu, CounterInterrogationCommand_getQCC(cic))) @@ -1308,13 +1309,14 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) if (cot == CS101_COT_REQUEST) { if (slave->readHandler != NULL) { - ReadCommand rc = (ReadCommand) CS101_ASDU_getElement(asdu, 0); + + union uInformationObject _io; + + ReadCommand rc = (ReadCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (slave->readHandler(slave->readHandlerParameter, &(self->iMasterConnection), asdu, InformationObject_getObjectAddress((InformationObject) rc))) messageHandled = true; - - ReadCommand_destroy(rc); } } else @@ -1330,7 +1332,9 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) if (slave->clockSyncHandler != NULL) { - ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElement(asdu, 0); + union uInformationObject _io; + + ClockSynchronizationCommand csc = (ClockSynchronizationCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); CP56Time2a newTime = ClockSynchronizationCommand_getTime(csc); @@ -1354,8 +1358,6 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) sendASDUInternal(self, asdu); } - ClockSynchronizationCommand_destroy(csc); - messageHandled = true; } } @@ -1386,13 +1388,14 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) if (cot == CS101_COT_ACTIVATION) { if (slave->resetProcessHandler != NULL) { - ResetProcessCommand rpc = (ResetProcessCommand) CS101_ASDU_getElement(asdu, 0); + + union uInformationObject _io; + + ResetProcessCommand rpc = (ResetProcessCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (slave->resetProcessHandler(slave->resetProcessHandlerParameter, &(self->iMasterConnection), asdu, ResetProcessCommand_getQRP(rpc))) messageHandled = true; - - ResetProcessCommand_destroy(rpc); } } @@ -1408,13 +1411,14 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) if ((cot == CS101_COT_ACTIVATION) || (cot == CS101_COT_SPONTANEOUS)) { if (slave->delayAcquisitionHandler != NULL) { - DelayAcquisitionCommand dac = (DelayAcquisitionCommand) CS101_ASDU_getElement(asdu, 0); + + union uInformationObject _io; + + DelayAcquisitionCommand dac = (DelayAcquisitionCommand) CS101_ASDU_getElementEx(asdu, (InformationObject) &_io, 0); if (slave->delayAcquisitionHandler(slave->delayAcquisitionHandlerParameter, &(self->iMasterConnection), asdu, DelayAcquisitionCommand_getDelay(dac))) messageHandled = true; - - DelayAcquisitionCommand_destroy(dac); } } else diff --git a/lib60870-C/src/inc/api/iec60870_common.h b/lib60870-C/src/inc/api/iec60870_common.h index a3cd6e44..3de456da 100644 --- a/lib60870-C/src/inc/api/iec60870_common.h +++ b/lib60870-C/src/inc/api/iec60870_common.h @@ -410,6 +410,17 @@ CS101_ASDU_getNumberOfElements(CS101_ASDU self); InformationObject CS101_ASDU_getElement(CS101_ASDU self, int index); +/** + * \brief Get the information object with the given index and store it in the provided information object instance + * + * \param io if not NULL use the provided information object instance to store the information, has to be of correct type. + * \param index the index of the information object (starting with 0) + * + * \return the information object, or NULL if there is no information object with the given index + */ +InformationObject +CS101_ASDU_getElementEx(CS101_ASDU self, InformationObject io, int index); + /** * \brief Create a new ASDU. The type ID will be derived from the first InformationObject that will be added * From 1aa0935ffa1da28cbce89303f862c7935d6673e9 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 27 Jul 2018 17:01:47 +0200 Subject: [PATCH 08/24] - added configuration option CONFIG_USE_SEMAPHORES --- lib60870-C/config/lib60870_config.h | 8 ++- lib60870-C/src/iec60870/cs101/cs101_queue.c | 8 +-- lib60870-C/src/iec60870/cs101/cs101_slave.c | 2 +- .../src/iec60870/cs104/cs104_connection.c | 27 ++++++--- lib60870-C/src/iec60870/cs104/cs104_slave.c | 58 ++++++++++--------- lib60870-C/src/inc/internal/cs101_queue.h | 4 +- 6 files changed, 63 insertions(+), 44 deletions(-) diff --git a/lib60870-C/config/lib60870_config.h b/lib60870-C/config/lib60870_config.h index e73f784d..9ca1da0f 100644 --- a/lib60870-C/config/lib60870_config.h +++ b/lib60870-C/config/lib60870_config.h @@ -21,11 +21,15 @@ /** * Compile the library to use threads. This will require semaphore support - * - * CONFIG_USE_THREADS = 0 not yet supported */ #define CONFIG_USE_THREADS 0 +/** + * Compile the library using semaphore to protect critical objects. + * Required when CONFIG_USE_THREADS = 1. + */ +#define CONFIG_USE_SEMAPHORES 1 + /** * Use a separate thread to call the callback functions. This allows the user * to have a more natural program flow in the callback function. Otherwise callback diff --git a/lib60870-C/src/iec60870/cs101/cs101_queue.c b/lib60870-C/src/iec60870/cs101/cs101_queue.c index fe47825c..0c549b9d 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_queue.c +++ b/lib60870-C/src/iec60870/cs101/cs101_queue.c @@ -46,7 +46,7 @@ CS101_Queue_initialize(CS101_Queue self, int maxQueueSize) BufferFrame_initialize(&(self->encodeFrame), NULL, 0); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) self->queueLock = Semaphore_create(1); #endif } @@ -54,7 +54,7 @@ CS101_Queue_initialize(CS101_Queue self, int maxQueueSize) void CS101_Queue_dispose(CS101_Queue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->queueLock); #endif } @@ -62,7 +62,7 @@ CS101_Queue_dispose(CS101_Queue self) inline void CS101_Queue_lock(CS101_Queue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif } @@ -70,7 +70,7 @@ CS101_Queue_lock(CS101_Queue self) inline void CS101_Queue_unlock(CS101_Queue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } diff --git a/lib60870-C/src/iec60870/cs101/cs101_slave.c b/lib60870-C/src/iec60870/cs101/cs101_slave.c index 44c80824..039ee86d 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_slave.c +++ b/lib60870-C/src/iec60870/cs101/cs101_slave.c @@ -37,7 +37,7 @@ #include "cs101_queue.h" #include "cs101_asdu_internal.h" -#if (CONFIG_USE_THREADS == 1) +#if ((CONFIG_USE_THREADS == 1) || (CONFIG_USE_SEMAPHORES == 1)) #include "hal_thread.h" #endif diff --git a/lib60870-C/src/iec60870/cs104/cs104_connection.c b/lib60870-C/src/iec60870/cs104/cs104_connection.c index 8e9bdfc7..63b37f22 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_connection.c +++ b/lib60870-C/src/iec60870/cs104/cs104_connection.c @@ -80,8 +80,12 @@ struct sCS104_Connection { int maxSentASDUs; /* maximum number of ASDU to be sent without confirmation - parameter k */ int oldestSentASDU; /* index of oldest entry in k-buffer */ int newestSentASDU; /* index of newest entry in k-buffer */ -#if (CONFIG_USE_THREADS == 1) + +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore sentASDUsLock; +#endif + +#if (CONFIG_USE_THREADS == 1) Thread connectionHandlingThread; #endif @@ -206,8 +210,11 @@ createConnection(const char* hostname, int tcpPort) self->rawMessageHandler = NULL; self->rawMessageHandlerParameter = NULL; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) self->sentASDUsLock = Semaphore_create(1); +#endif + +#if (CONFIG_USE_THREADS == 1) self->connectionHandlingThread = NULL; #endif @@ -291,7 +298,7 @@ resetConnection(CS104_Connection self) static bool checkSequenceNumber(CS104_Connection self, int seqNo) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -370,7 +377,7 @@ checkSequenceNumber(CS104_Connection self, int seqNo) } } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif @@ -413,7 +420,7 @@ CS104_Connection_destroy(CS104_Connection self) if (self->sentASDUs != NULL) GLOBAL_FREEMEM(self->sentASDUs); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->sentASDUsLock); #endif @@ -662,7 +669,7 @@ handleTimeouts(CS104_Connection self) } /* check if counterpart confirmed I messages */ -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif if (self->oldestSentASDU != -1) { @@ -671,7 +678,7 @@ handleTimeouts(CS104_Connection self) retVal = false; } } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif @@ -681,6 +688,7 @@ handleTimeouts(CS104_Connection self) return retVal; } +#if (CONFIG_USE_THREADS == 1) static void* handleConnection(void* parameter) { @@ -786,6 +794,7 @@ handleConnection(void* parameter) return NULL; } +#endif /* (CONFIG_USE_THREADS == 1) */ void CS104_Connection_connectAsync(CS104_Connection self) @@ -875,7 +884,7 @@ CS104_Connection_sendStopDT(CS104_Connection self) static void sendIMessageAndUpdateSentASDUs(CS104_Connection self, Frame frame) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -894,7 +903,7 @@ sendIMessageAndUpdateSentASDUs(CS104_Connection self, Frame frame) self->newestSentASDU = currentIndex; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif } diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 9bd4bb85..70506007 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -119,7 +119,7 @@ struct sMessageQueue { ASDUQueueEntry asdus; #endif -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore queueLock; #endif }; @@ -139,7 +139,7 @@ MessageQueue_initialize(MessageQueue self, int maxQueueSize) self->lastMsgIndex = 0; self->size = maxQueueSize; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) self->queueLock = Semaphore_create(1); #endif } @@ -163,7 +163,7 @@ MessageQueue_destroy(MessageQueue self) GLOBAL_FREEMEM(self->asdus); #endif -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->queueLock); #endif @@ -174,7 +174,7 @@ MessageQueue_destroy(MessageQueue self) static void MessageQueue_lock(MessageQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif } @@ -182,7 +182,7 @@ MessageQueue_lock(MessageQueue self) static void MessageQueue_unlock(MessageQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } @@ -194,7 +194,7 @@ MessageQueue_unlock(MessageQueue self) static void MessageQueue_enqueueASDU(MessageQueue self, CS101_ASDU asdu) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -247,7 +247,7 @@ MessageQueue_enqueueASDU(MessageQueue self, CS101_ASDU asdu) DEBUG_PRINT("ASDUs in FIFO: %i (first: %i, last: %i)\n", self->entryCounter, self->firstMsgIndex, self->lastMsgIndex); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } @@ -255,7 +255,7 @@ MessageQueue_enqueueASDU(MessageQueue self, CS101_ASDU asdu) static bool MessageQueue_isAsduAvailable(MessageQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -266,7 +266,7 @@ MessageQueue_isAsduAvailable(MessageQueue self) else retVal = false; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif @@ -311,7 +311,7 @@ MessageQueue_getNextWaitingASDU(MessageQueue self, uint64_t* timestamp, int* que static void MessageQueue_releaseAllQueuedASDUs(MessageQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -319,7 +319,7 @@ MessageQueue_releaseAllQueuedASDUs(MessageQueue self) self->lastMsgIndex = 0; self->entryCounter = 0; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } @@ -331,7 +331,7 @@ MessageQueue_markAsduAsConfirmed(MessageQueue self, int queueIndex, uint64_t tim if ((queueIndex < 0) || (queueIndex > self->size)) return; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -382,7 +382,7 @@ MessageQueue_markAsduAsConfirmed(MessageQueue self, int queueIndex, uint64_t tim } } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } @@ -403,7 +403,7 @@ struct sHighPriorityASDUQueue { FrameBuffer* asdus; #endif -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore queueLock; #endif }; @@ -424,7 +424,7 @@ HighPriorityASDUQueue_initialize(HighPriorityASDUQueue self, int maxQueueSize) self->lastMsgIndex = 0; self->size = maxQueueSize; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) self->queueLock = Semaphore_create(1); #endif } @@ -448,7 +448,7 @@ HighPriorityASDUQueue_destroy(HighPriorityASDUQueue self) GLOBAL_FREEMEM(self->asdus); #endif -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->queueLock); #endif @@ -458,7 +458,7 @@ HighPriorityASDUQueue_destroy(HighPriorityASDUQueue self) static void HighPriorityASDUQueue_lock(HighPriorityASDUQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif } @@ -466,7 +466,7 @@ HighPriorityASDUQueue_lock(HighPriorityASDUQueue self) static void HighPriorityASDUQueue_unlock(HighPriorityASDUQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } @@ -474,7 +474,7 @@ HighPriorityASDUQueue_unlock(HighPriorityASDUQueue self) static bool HighPriorityASDUQueue_isAsduAvailable(HighPriorityASDUQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -485,7 +485,7 @@ HighPriorityASDUQueue_isAsduAvailable(HighPriorityASDUQueue self) else retVal = false; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif @@ -520,7 +520,7 @@ HighPriorityASDUQueue_getNextASDU(HighPriorityASDUQueue self) static bool HighPriorityASDUQueue_enqueue(HighPriorityASDUQueue self, CS101_ASDU asdu) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -563,7 +563,7 @@ HighPriorityASDUQueue_enqueue(HighPriorityASDUQueue self, CS101_ASDU asdu) exit_function: -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif @@ -573,7 +573,7 @@ HighPriorityASDUQueue_enqueue(HighPriorityASDUQueue self, CS101_ASDU asdu) static void HighPriorityASDUQueue_resetConnectionQueue(HighPriorityASDUQueue self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->queueLock); #endif @@ -581,7 +581,7 @@ HighPriorityASDUQueue_resetConnectionQueue(HighPriorityASDUQueue self) self->lastMsgIndex = 0; self->entryCounter = 0; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->queueLock); #endif } @@ -638,8 +638,11 @@ struct sCS104_Slave { int openConnections; /**< number of connected clients */ LinkedList masterConnections; /**< references to all MasterConnection objects */ -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore openConnectionsLock; +#endif + +#if (CONFIG_USE_THREADS == 1) bool isThreadlessMode; #endif @@ -748,8 +751,11 @@ createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) self->masterConnections = LinkedList_create(); self->maxOpenConnections = CONFIG_CS104_MAX_CLIENT_CONNECTIONS; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) self->openConnectionsLock = Semaphore_create(1); +#endif + +#if (CONFIG_USE_THREADS == 1) self->isThreadlessMode = false; #endif diff --git a/lib60870-C/src/inc/internal/cs101_queue.h b/lib60870-C/src/inc/internal/cs101_queue.h index a61638a2..2ff958fb 100644 --- a/lib60870-C/src/inc/internal/cs101_queue.h +++ b/lib60870-C/src/inc/internal/cs101_queue.h @@ -28,7 +28,7 @@ extern "C" { #endif -#if (CONFIG_USE_THREADS == 1) +#if ((CONFIG_USE_THREADS == 1) || (CONFIG_USE_SEMAPHORES == 1)) #include "hal_thread.h" #endif @@ -58,7 +58,7 @@ struct sCS101_Queue { struct sCS101_QueueElement elements[CS101_MAX_QUEUE_SIZE]; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore queueLock; #endif }; From bb8e53298dbe9d7d9a88a1baadf5b30389c1fdec Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 8 Aug 2018 17:08:15 +0200 Subject: [PATCH 09/24] - added test cases for EventOfProtectionEquipmentWithCP56Time2a --- lib60870-C/tests/all_tests.c | 75 ++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/lib60870-C/tests/all_tests.c b/lib60870-C/tests/all_tests.c index 9217e0b6..9a201da7 100644 --- a/lib60870-C/tests/all_tests.c +++ b/lib60870-C/tests/all_tests.c @@ -1,10 +1,26 @@ #include "unity.h" #include "iec60870_common.h" #include "hal_time.h" +#include "buffer_frame.h" void setUp(void) { } void tearDown(void) {} +static struct sCS101_AppLayerParameters defaultAppLayerParameters = { + /* .sizeOfTypeId = */ 1, + /* .sizeOfVSQ = */ 1, + /* .sizeOfCOT = */ 2, + /* .originatorAddress = */ 0, + /* .sizeOfCA = */ 2, + /* .sizeOfIOA = */ 3, + /* .maxSizeOfASDU = */ 249 +}; + +void +CS101_ASDU_encode(CS101_ASDU self, Frame frame); + +CS101_ASDU +CS101_ASDU_createFromBuffer(CS101_AppLayerParameters parameters, uint8_t* msg, int msgLength); void test_CP56Time2a(void) @@ -107,6 +123,63 @@ test_addMaxNumberOfIOsToASDU(void) } +void +test_SingleEventType(void) +{ + tSingleEvent singleEvent = 0; + + EventState eventState = SingleEvent_getEventState(&singleEvent); + + TEST_ASSERT_EQUAL_INT(IEC60870_EVENTSTATE_INDETERMINATE_0, eventState); + + QualityDescriptorP qdp = SingleEvent_getQDP(&singleEvent); + + TEST_ASSERT_EQUAL_INT(0, qdp); +} + +void +test_EventOfProtectionEquipmentWithTime(void) +{ + tSingleEvent singleEvent = 0; + struct sCP16Time2a elapsedTime; + struct sCP56Time2a timestamp; + + EventOfProtectionEquipmentWithCP56Time2a e = EventOfProtectionEquipmentWithCP56Time2a_create(NULL, 1, &singleEvent, &elapsedTime, ×tamp); + + uint8_t buffer[256]; + + struct sBufferFrame bf; + + Frame f = BufferFrame_initialize(&bf, buffer, 0); + + CS101_ASDU asdu = CS101_ASDU_create(&defaultAppLayerParameters, false, CS101_COT_PERIODIC, 0, 1, false, false); + + CS101_ASDU_addInformationObject(asdu, (InformationObject) e); + CS101_ASDU_addInformationObject(asdu, (InformationObject) e); + + CS101_ASDU_encode(asdu, f); + + InformationObject_destroy(e); + CS101_ASDU_destroy(asdu); + + CS101_ASDU asdu2 = CS101_ASDU_createFromBuffer(&defaultAppLayerParameters, buffer, Frame_getMsgSize(f)); + + InformationObject io = CS101_ASDU_getElement(asdu2, 1); + + TEST_ASSERT_NOT_NULL(io); + + EventOfProtectionEquipmentWithCP56Time2a e2 = (EventOfProtectionEquipmentWithCP56Time2a) io; + + SingleEvent se = EventOfProtectionEquipmentWithCP56Time2a_getEvent(e2); + + QualityDescriptorP qdp = SingleEvent_getQDP(se); + + InformationObject_destroy(io); + CS101_ASDU_destroy(asdu2); + + TEST_ASSERT_EQUAL_INT(0, qdp); +} + int main(int argc, char** argv) { @@ -115,5 +188,7 @@ main(int argc, char** argv) RUN_TEST(test_CP56Time2aToMsTimestamp); RUN_TEST(test_StepPositionInformation); RUN_TEST(test_addMaxNumberOfIOsToASDU); + RUN_TEST(test_SingleEventType); + RUN_TEST(test_EventOfProtectionEquipmentWithTime); return UNITY_END(); } From cc3c361d1bc93f049ea67b051f847cceb18add64 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 8 Aug 2018 17:17:06 +0200 Subject: [PATCH 10/24] - CS104 slave: separated thread and semaphore support options --- lib60870-C/src/iec60870/cs104/cs104_slave.c | 101 ++++++++++---------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 70506007..faf859b8 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -835,13 +835,13 @@ CS104_Slave_getOpenConnections(CS104_Slave self) { int openConnections; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->openConnectionsLock); #endif openConnections = self->openConnections; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->openConnectionsLock); #endif @@ -883,7 +883,7 @@ CS104_Slave_activate(CS104_Slave self, MasterConnection connectionToActivate) if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) { /* Deactivate all other connections */ -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->openConnectionsLock); #endif @@ -899,7 +899,7 @@ CS104_Slave_activate(CS104_Slave self, MasterConnection connectionToActivate) MasterConnection_deactivate(connection); } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->openConnectionsLock); #endif @@ -1007,7 +1007,7 @@ struct sMasterConnection { int oldestSentASDU; int newestSentASDU; SentASDUSlave* sentASDUs; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore sentASDUsLock; #endif @@ -1139,7 +1139,7 @@ sendIMessage(MasterConnection self, uint8_t* buffer, int msgSize) buffer[4] = (uint8_t) ((self->receiveCount % 128) * 2); buffer[5] = (uint8_t) (self->receiveCount / 128); - if (writeToSocket(self, buffer, msgSize) != -1) { + if (writeToSocket(self, buffer, msgSize) > 0) { DEBUG_PRINT("SEND I (size = %i) N(S) = %i N(R) = %i\n", msgSize, self->sendCount, self->receiveCount); self->sendCount = (self->sendCount + 1) % 32768; self->unconfirmedReceivedIMessages = 0; @@ -1200,7 +1200,7 @@ sendASDUInternal(MasterConnection self, CS101_ASDU asdu) if (self->isActive) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -1217,14 +1217,14 @@ sendASDUInternal(MasterConnection self, CS101_ASDU asdu) sendASDU(self, &frameBuffer, 0, -1); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif asduSent = true; } else { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif asduSent = HighPriorityASDUQueue_enqueue(self->highPrioQueue, asdu); @@ -1453,7 +1453,7 @@ handleASDU(MasterConnection self, CS101_ASDU asdu) static bool checkSequenceNumber(MasterConnection self, int seqNo) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -1543,7 +1543,7 @@ checkSequenceNumber(MasterConnection self, int seqNo) DEBUG_PRINT("Received sequence number out of range"); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif @@ -1609,7 +1609,8 @@ handleMessage(MasterConnection self, uint8_t* buffer, int msgSize) else if ((buffer[2] & 0x43) == 0x43) { DEBUG_PRINT("Send TESTFR_CON\n"); - writeToSocket(self, TESTFR_CON_MSG, TESTFR_CON_MSG_SIZE); + if (writeToSocket(self, TESTFR_CON_MSG, TESTFR_CON_MSG_SIZE) < 0) + return false; } /* Check for STARTDT_ACT message */ @@ -1620,7 +1621,8 @@ handleMessage(MasterConnection self, uint8_t* buffer, int msgSize) DEBUG_PRINT("Send STARTDT_CON\n"); - writeToSocket(self, STARTDT_CON_MSG, STARTDT_CON_MSG_SIZE); + if (writeToSocket(self, STARTDT_CON_MSG, STARTDT_CON_MSG_SIZE) < 0) + return false; } /* Check for STOPDT_ACT message */ @@ -1629,7 +1631,8 @@ handleMessage(MasterConnection self, uint8_t* buffer, int msgSize) DEBUG_PRINT("Send STOPDT_CON\n"); - writeToSocket(self, STOPDT_CON_MSG, STOPDT_CON_MSG_SIZE); + if (writeToSocket(self, STOPDT_CON_MSG, STOPDT_CON_MSG_SIZE) < 0) + return false; } /* Check for TESTFR_CON message */ @@ -1672,7 +1675,8 @@ sendSMessage(MasterConnection self) msg[4] = (uint8_t) ((self->receiveCount % 128) * 2); msg[5] = (uint8_t) (self->receiveCount / 128); - writeToSocket(self, msg, 6); + if (writeToSocket(self, msg, 6) < 0) + self->isRunning = false; } static void @@ -1689,7 +1693,7 @@ MasterConnection_destroy(MasterConnection self) GLOBAL_FREEMEM(self->sentASDUs); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->sentASDUsLock); #endif @@ -1703,7 +1707,7 @@ MasterConnection_destroy(MasterConnection self) static void sendNextLowPriorityASDU(MasterConnection self) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -1726,7 +1730,7 @@ sendNextLowPriorityASDU(MasterConnection self) exit_function: -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif @@ -1740,7 +1744,7 @@ sendNextHighPriorityASDU(MasterConnection self) FrameBuffer* msg; -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -1759,7 +1763,7 @@ sendNextHighPriorityASDU(MasterConnection self) HighPriorityASDUQueue_unlock(self->highPrioQueue); exit_function: -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif @@ -1811,7 +1815,7 @@ handleTimeouts(MasterConnection self) timeoutsOk = false; } else { - if (writeToSocket(self, TESTFR_ACT_MSG, TESTFR_ACT_MSG_SIZE) == -1) { + if (writeToSocket(self, TESTFR_ACT_MSG, TESTFR_ACT_MSG_SIZE) < 0) { DEBUG_PRINT("Failed to write TESTFR ACT message\n"); @@ -1833,7 +1837,7 @@ handleTimeouts(MasterConnection self) } } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->sentASDUsLock); #endif @@ -1855,7 +1859,7 @@ handleTimeouts(MasterConnection self) } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->sentASDUsLock); #endif @@ -1865,7 +1869,7 @@ handleTimeouts(MasterConnection self) static void CS104_Slave_removeConnection(CS104_Slave self, MasterConnection connection) { -#if (CONFIG_USE_THREADS) +#if (CONFIG_USE_SEMAPHORES) Semaphore_wait(self->openConnectionsLock); #endif @@ -1874,7 +1878,7 @@ CS104_Slave_removeConnection(CS104_Slave self, MasterConnection connection) MasterConnection_destroy(connection); -#if (CONFIG_USE_THREADS) +#if (CONFIG_USE_SEMAPHORES) Semaphore_post(self->openConnectionsLock); #endif @@ -2050,7 +2054,7 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu resetT3Timeout(self); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) self->sentASDUsLock = Semaphore_create(1); #endif @@ -2059,7 +2063,7 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu self->tlsSocket = TLSSocket_create(socket, slave->tlsConfig); if (self->tlsSocket == NULL) { - printf("Close connection\n"); + DEBUG_PRINT("Close connection\n"); MasterConnection_destroy(self); return NULL; @@ -2125,17 +2129,16 @@ MasterConnection_handleTcpConnection(MasterConnection self) { int bytesRec = receiveMessage(self, self->buffer); - if (self->slave->rawMessageHandler) - self->slave->rawMessageHandler(self->slave->rawMessageHandlerParameter, - &(self->iMasterConnection), self->buffer, bytesRec, false); - - if (bytesRec == -1) { + if (bytesRec < 0) { DEBUG_PRINT("Error reading from socket\n"); self->isRunning = false; } - if (bytesRec > 0) { - DEBUG_PRINT("Connection: rcvd msg(%i bytes)\n", bytesRec); + if ((bytesRec > 0) && (self->isRunning)) { + + if (self->slave->rawMessageHandler) + self->slave->rawMessageHandler(self->slave->rawMessageHandlerParameter, + &(self->iMasterConnection), self->buffer, bytesRec, false); if (handleMessage(self, self->buffer, bytesRec) == false) self->isRunning = false; @@ -2234,7 +2237,8 @@ handleClientConnections(CS104_Slave self) MasterConnection con = (MasterConnection) LinkedList_getData(connection); - MasterConnection_executePeriodicTasks(con); + if (con->isRunning) + MasterConnection_executePeriodicTasks(con); connection = LinkedList_getNext(connection); } @@ -2294,22 +2298,22 @@ handleConnectionsThreadless(CS104_Slave self) if (connection) { -#if (CONFIG_USE_THREADS) +#if (CONFIG_USE_SEMAPHORES) Semaphore_wait(self->openConnectionsLock); #endif self->openConnections++; LinkedList_add(self->masterConnections, connection); -#if (CONFIG_USE_THREADS) +#if (CONFIG_USE_SEMAPHORES) Semaphore_post(self->openConnectionsLock); #endif - - - /* now start the connection handling (thread) */ - //MasterConnection_start(connection); connection->isRunning = true; + + if (self->connectionEventHandler) { + self->connectionEventHandler(self->connectionEventHandlerParameter, &(connection->iMasterConnection), CS104_CON_EVENT_CONNECTION_OPENED); + } } else DEBUG_PRINT("Connection attempt failed!"); @@ -2322,7 +2326,6 @@ handleConnectionsThreadless(CS104_Slave self) } - handleClientConnections(self); } @@ -2398,14 +2401,14 @@ serverThread (void* parameter) if (connection) { -#if (CONFIG_USE_THREADS) +#if (CONFIG_USE_SEMAPHORES) Semaphore_wait(self->openConnectionsLock); #endif self->openConnections++; LinkedList_add(self->masterConnections, connection); -#if (CONFIG_USE_THREADS) +#if (CONFIG_USE_SEMAPHORES) Semaphore_post(self->openConnectionsLock); #endif @@ -2445,7 +2448,7 @@ CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu) #if (CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP == 1) if (self->serverMode == CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP) { -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->openConnectionsLock); #endif @@ -2464,7 +2467,7 @@ CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu) MessageQueue_enqueueASDU(connection->lowPrioQueue, asdu); } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->openConnectionsLock); #endif } @@ -2595,7 +2598,7 @@ CS104_Slave_destroy(CS104_Slave self) /* * Stop all connections * */ -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->openConnectionsLock); #endif @@ -2610,7 +2613,7 @@ CS104_Slave_destroy(CS104_Slave self) MasterConnection_close(connection); } -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_post(self->openConnectionsLock); #endif @@ -2636,7 +2639,7 @@ CS104_Slave_destroy(CS104_Slave self) LinkedList_destroyStatic(self->masterConnections); -#if (CONFIG_USE_THREADS == 1) +#if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->openConnectionsLock); #endif From f4a81906bb0c464a4a0d1c1f546f546c4144e9a2 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 8 Aug 2018 20:50:11 +0200 Subject: [PATCH 11/24] - TLS: fixed problem with blocking TLS socket --- lib60870-C/config/lib60870_config.h | 2 +- lib60870-C/src/tls/mbedtls/tls_mbedtls.c | 73 +++++++++--------------- 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/lib60870-C/config/lib60870_config.h b/lib60870-C/config/lib60870_config.h index a55a4ec8..06bff912 100644 --- a/lib60870-C/config/lib60870_config.h +++ b/lib60870-C/config/lib60870_config.h @@ -22,7 +22,7 @@ /** * Compile the library to use threads. This will require semaphore support */ -#define CONFIG_USE_THREADS 0 +#define CONFIG_USE_THREADS 1 /** * Compile the library using semaphore to protect critical objects. diff --git a/lib60870-C/src/tls/mbedtls/tls_mbedtls.c b/lib60870-C/src/tls/mbedtls/tls_mbedtls.c index 24c01641..3b8ea7e7 100644 --- a/lib60870-C/src/tls/mbedtls/tls_mbedtls.c +++ b/lib60870-C/src/tls/mbedtls/tls_mbedtls.c @@ -191,7 +191,7 @@ TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int ret = mbedtls_x509_crt_parse(&(self->ownCertificate), certificate, certLen); if (ret != 0) - printf("mbedtls_x509_crt_parse returned %d\n", ret); + DEBUG_PRINT("mbedtls_x509_crt_parse returned %d\n", ret); return (ret == 0); } @@ -371,47 +371,30 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration) int TLSSocket_read(TLSSocket self, uint8_t* buf, int size) { - int ret; - int len = size; - - do - { - ret = mbedtls_ssl_read( &(self->ssl), buf, len ); - - if( ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE ) - continue; + int ret = mbedtls_ssl_read(&(self->ssl), buf, size); - if( ret <= 0 ) - { - switch( ret ) - { - case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: - DEBUG_PRINT("TLS", " connection was closed gracefully\n" ); - len = -1; - break; + if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) + return 0; - case MBEDTLS_ERR_NET_CONN_RESET: - len = -1; - DEBUG_PRINT("TLS", " connection was reset by peer\n" ); - break; + if (ret < 0) { - default: - DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret ); - len = -1; //TODO is this the correct return value? - break; - } - - break; - } + switch (ret) + { + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + DEBUG_PRINT("TLS", " connection was closed gracefully\n"); + return -1; - len = ret; + case MBEDTLS_ERR_NET_CONN_RESET: + DEBUG_PRINT("TLS", " connection was reset by peer\n"); + return -1; - if( ret > 0 ) - break; - } - while( 1 ); + default: + DEBUG_PRINT("TLS", " mbedtls_ssl_read returned -0x%x\n", -ret); + return -1; + } + } - return len; + return ret; } int @@ -420,18 +403,17 @@ TLSSocket_write(TLSSocket self, uint8_t* buf, int size) int ret; int len = size; - - while( ( ret = mbedtls_ssl_write( &(self->ssl), buf, len ) ) <= 0 ) + while ((ret = mbedtls_ssl_write(&(self->ssl), buf, len)) <= 0) { - if( ret == MBEDTLS_ERR_NET_CONN_RESET ) + if (ret == MBEDTLS_ERR_NET_CONN_RESET) { - DEBUG_PRINT("TLS", "peer closed the connection\n" ); + DEBUG_PRINT("TLS", "peer closed the connection\n"); return -1; } - if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { - DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret ); + DEBUG_PRINT("TLS", "mbedtls_ssl_write returned %d\n", ret); return -1; } } @@ -448,12 +430,11 @@ TLSSocket_close(TLSSocket self) //TODO add timeout? - while( ( ret = mbedtls_ssl_close_notify( &(self->ssl) ) ) < 0 ) + while ((ret = mbedtls_ssl_close_notify(&(self->ssl))) < 0) { - if( ret != MBEDTLS_ERR_SSL_WANT_READ && - ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) { - DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret ); + DEBUG_PRINT("TLS", "mbedtls_ssl_close_notify returned %d\n", ret); break; } } From 1a42c142eaa368076a945abcb7f36606cbeeadb6 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 8 Aug 2018 21:04:06 +0200 Subject: [PATCH 12/24] - removed warning in test - updated CHANGELOG --- CHANGELOG | 11 +++++++++++ lib60870-C/tests/all_tests.c | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 74371117..820bcef2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,14 @@ +Changes to version 2.1.0 +------------------------ +- added non-threaded moded for CS 104 slave +- separated thread and semaphore support for CS 104 slave +- CS101 unbalanced link layer (master): automatically send request UD 1 when ACD bit is set in received frame +- added CS101_ASDU_getElementEx function to avoid dynamic memory allocation +- added support for static ASDU object allocation +- fixed length check when parsing M_EP_TD_1 +- CS101 unbalanced master: fixed state machine problem with multiple slaves (some responses don't change state and master keeps locked on the slave) +- optionally compile library without HAL to simply using together with libiec61850 + Changes to version 2.0.2 ------------------------ - CS104 slave: added new CS104_ConnectionEventHandler to track connection events diff --git a/lib60870-C/tests/all_tests.c b/lib60870-C/tests/all_tests.c index 9a201da7..70e60672 100644 --- a/lib60870-C/tests/all_tests.c +++ b/lib60870-C/tests/all_tests.c @@ -159,7 +159,7 @@ test_EventOfProtectionEquipmentWithTime(void) CS101_ASDU_encode(asdu, f); - InformationObject_destroy(e); + InformationObject_destroy((InformationObject) e); CS101_ASDU_destroy(asdu); CS101_ASDU asdu2 = CS101_ASDU_createFromBuffer(&defaultAppLayerParameters, buffer, Frame_getMsgSize(f)); From 76e3677c542619ccefeb1966415978465fb404ed Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 8 Aug 2018 22:03:22 +0200 Subject: [PATCH 13/24] - CS 101 slave: added function CS101_Slave_setDIR to configure DIR bit in balanced mode --- lib60870-C/src/iec60870/cs101/cs101_slave.c | 8 ++++++++ lib60870-C/src/inc/api/cs101_slave.h | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/lib60870-C/src/iec60870/cs101/cs101_slave.c b/lib60870-C/src/iec60870/cs101/cs101_slave.c index 039ee86d..d31dd699 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_slave.c +++ b/lib60870-C/src/iec60870/cs101/cs101_slave.c @@ -360,6 +360,14 @@ CS101_Slave_destroy(CS101_Slave self) } } +void +CS101_Slave_setDIR(CS101_Slave self, bool dir) +{ + if (self->linkLayerMode == IEC60870_LINK_LAYER_BALANCED) { + LinkLayerBalanced_setDIR(self->balancedLinkLayer, dir); + } +} + void CS101_Slave_setIdleTimeout(CS101_Slave self, int timeoutInMs) { diff --git a/lib60870-C/src/inc/api/cs101_slave.h b/lib60870-C/src/inc/api/cs101_slave.h index 51b7bbd1..38621196 100644 --- a/lib60870-C/src/inc/api/cs101_slave.h +++ b/lib60870-C/src/inc/api/cs101_slave.h @@ -81,6 +81,17 @@ CS101_Slave_create(SerialPort serialPort, LinkLayerParameters llParameters, CS10 void CS101_Slave_destroy(CS101_Slave self); +/** + * \brief Set the value of the DIR bit when sending messages (only balanced mode) + * + * NOTE: Default value is false (controlled station). In the case of two equivalent stations + * the value is defined by agreement. + * + * \param dir the value of the DIR bit when sending messages + */ +void +CS101_Slave_setDIR(CS101_Slave self, bool dir); + /** * \brief Set the idle timeout * From 17d75e8d97ef44a56dc9177c50b03b4f7e005b10 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 9 Aug 2018 09:43:07 +0200 Subject: [PATCH 14/24] - tls client example: accept hostname as command line argument --- lib60870-C/examples/tls_client/tls_client.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib60870-C/examples/tls_client/tls_client.c b/lib60870-C/examples/tls_client/tls_client.c index 4ba22c13..0a6234fa 100644 --- a/lib60870-C/examples/tls_client/tls_client.c +++ b/lib60870-C/examples/tls_client/tls_client.c @@ -80,6 +80,12 @@ asduReceivedHandler (void* parameter, int address, CS101_ASDU asdu) int main(int argc, char** argv) { + char* hostname = "127.0.0.1"; + + if (argc > 1) { + hostname = argv[1]; + } + TLSConfiguration tlsConfig = TLSConfiguration_create(); TLSConfiguration_setChainValidation(tlsConfig, true); @@ -91,7 +97,7 @@ main(int argc, char** argv) TLSConfiguration_addAllowedCertificateFromFile(tlsConfig, "server.cer"); - CS104_Connection con = CS104_Connection_createSecure("127.0.0.1", IEC_60870_5_104_DEFAULT_TLS_PORT, tlsConfig); + CS104_Connection con = CS104_Connection_createSecure(hostname, IEC_60870_5_104_DEFAULT_TLS_PORT, tlsConfig); CS104_Connection_setConnectionHandler(con, connectionHandler, NULL); CS104_Connection_setASDUReceivedHandler(con, asduReceivedHandler, NULL); From 968d41fc21fd8f5bac651d82b71fd1323d6ed2d6 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 9 Aug 2018 16:00:31 +0200 Subject: [PATCH 15/24] - refactored platform abstraction layer (added memory and TLS) - made compilation of platform abstraction layer and common code optional --- README.md | 12 +- lib60870-C/Makefile | 21 ++- lib60870-C/src/common/linked_list.c | 4 + lib60870-C/src/hal/inc/hal_socket.h | 8 +- .../src/{common => hal}/inc/lib_memory.h | 0 lib60870-C/src/hal/inc/tls_config.h | 88 ++++++++++++ lib60870-C/src/hal/inc/tls_socket.h | 108 +++++++++++++++ .../src/{common => hal/memory}/lib_memory.c | 0 .../{ => hal}/tls/mbedtls/mbedtls_config.h | 0 .../src/{ => hal}/tls/mbedtls/tls_mbedtls.c | 2 +- lib60870-C/src/iec60870/cs104/cs104_slave.c | 2 +- lib60870-C/src/inc/api/cs104_connection.h | 3 +- lib60870-C/src/inc/api/iec60870_slave.h | 2 +- lib60870-C/src/tls/tls_api.h | 128 ------------------ 14 files changed, 238 insertions(+), 140 deletions(-) rename lib60870-C/src/{common => hal}/inc/lib_memory.h (100%) create mode 100644 lib60870-C/src/hal/inc/tls_config.h create mode 100644 lib60870-C/src/hal/inc/tls_socket.h rename lib60870-C/src/{common => hal/memory}/lib_memory.c (100%) rename lib60870-C/src/{ => hal}/tls/mbedtls/mbedtls_config.h (100%) rename lib60870-C/src/{ => hal}/tls/mbedtls/tls_mbedtls.c (99%) delete mode 100644 lib60870-C/src/tls/tls_api.h diff --git a/README.md b/README.md index 0edcde4e..5134e4b8 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,16 @@ the project folder and run cmake to create the build files: `cd build` `cmake ..` +## Building without common code and HAL + +The library contains some common code and a platform abstraction layer (HAL) that is shared with +other protocol libraries of MZ Automation (e.g. libiec61850). In order to simplify using these +protocol libraries together it is possible to compile the library without the common parts. + +This can be done by using the *WITHOUT_HAL* and *WITHOUT_COMMON* defines when calling make: + +`make WITHOUT_HAL=1 WITHOUT_COMMON=1` + ## Building with TLS support The library can be build with support for TLS. In order to do so you have to download mbedtls version 2.6.0. @@ -49,7 +59,7 @@ The cmake build system will automatically detect the mbedtls source and build th When using make you have to call make with WITH_MBEDTLS=1 -make WITH_MBEDTLS=1 +`make WITH_MBEDTLS=1` ## Contact: diff --git a/lib60870-C/Makefile b/lib60870-C/Makefile index 4e8a02d5..358a6a63 100644 --- a/lib60870-C/Makefile +++ b/lib60870-C/Makefile @@ -2,42 +2,53 @@ LIB60870_HOME=. include make/target_system.mk +ifndef WITHOUT_COMMON + LIB_SOURCE_DIRS = src/common + +endif + LIB_SOURCE_DIRS += src/iec60870 LIB_SOURCE_DIRS += src/iec60870/cs101 LIB_SOURCE_DIRS += src/iec60870/cs104 LIB_SOURCE_DIRS += src/iec60870/link_layer LIB_SOURCE_DIRS += src/iec60870/apl +ifndef WITHOUT_HAL + ifeq ($(HAL_IMPL), WIN32) LIB_SOURCE_DIRS += src/hal/socket/win32 LIB_SOURCE_DIRS += src/hal/thread/win32 LIB_SOURCE_DIRS += src/hal/time/win32 +LIB_SOURCE_DIRS += src/hal/memory else ifeq ($(HAL_IMPL), POSIX) LIB_SOURCE_DIRS += src/hal/socket/linux LIB_SOURCE_DIRS += src/hal/thread/linux LIB_SOURCE_DIRS += src/hal/time/unix LIB_SOURCE_DIRS += src/hal/serial/linux +LIB_SOURCE_DIRS += src/hal/memory else ifeq ($(HAL_IMPL), BSD) LIB_SOURCE_DIRS += src/hal/socket/bsd LIB_SOURCE_DIRS += src/hal/thread/bsd LIB_SOURCE_DIRS += src/hal/time/unix +LIB_SOURCE_DIRS += src/hal/memory endif ifdef WITH_MBEDTLS LIB_SOURCE_DIRS += dependencies/mbedtls-2.6.0/library -LIB_SOURCE_DIRS += src/tls/mbedtls -LIB_INCLUDE_DIRS += src/tls/mbedtls +LIB_SOURCE_DIRS += src/hal/tls/mbedtls +LIB_INCLUDE_DIRS += src/hal/tls/mbedtls LIB_INCLUDE_DIRS += dependencies/mbedtls-2.6.0/include CFLAGS += -D'MBEDTLS_CONFIG_FILE="mbedtls_config.h"' CFLAGS += -D'CONFIG_CS104_SUPPORT_TLS=1' endif +endif + LIB_INCLUDE_DIRS += config LIB_INCLUDE_DIRS += src/inc/api LIB_INCLUDE_DIRS += src/inc/internal LIB_INCLUDE_DIRS += src/hal/inc -LIB_INCLUDE_DIRS += src/tls LIB_INCLUDE_DIRS += src/common/inc @@ -50,12 +61,14 @@ endif LIB_API_HEADER_FILES = src/hal/inc/hal_time.h LIB_API_HEADER_FILES += src/hal/inc/hal_thread.h LIB_API_HEADER_FILES += src/hal/inc/hal_socket.h +LIB_API_HEADER_FILES += src/hal/tls/tls_socket.h +LIB_API_HEADER_FILES += src/hal/tls/tls_config.h LIB_API_HEADER_FILES += src/inc/api/iec60870_master.h LIB_API_HEADER_FILES += src/inc/api/iec60870_server.h LIB_API_HEADER_FILES += src/inc/api/iec60870_common.h LIB_API_HEADER_FILES += src/inc/api/information_objects.h LIB_API_HEADER_FILES += src/inc/api/t104_connection.h -LIB_API_HEADER_FILES += src/tls/tls_api.h + LIB_TEST_SOURCES = tests/all_tests.c LIB_TEST_SOURCES += tests/unity/unity.c diff --git a/lib60870-C/src/common/linked_list.c b/lib60870-C/src/common/linked_list.c index 87df55f8..aa352559 100644 --- a/lib60870-C/src/common/linked_list.c +++ b/lib60870-C/src/common/linked_list.c @@ -22,6 +22,8 @@ #include "linked_list.h" #include "lib_memory.h" +#ifndef CONFIG_COMPILE_WITHOUT_COMMON + LinkedList LinkedList_getLastElement(LinkedList list) { @@ -180,3 +182,5 @@ LinkedList_getData(LinkedList self) return self->data; } +#endif + diff --git a/lib60870-C/src/hal/inc/hal_socket.h b/lib60870-C/src/hal/inc/hal_socket.h index 24b064fa..4ffd3ca3 100644 --- a/lib60870-C/src/hal/inc/hal_socket.h +++ b/lib60870-C/src/hal/inc/hal_socket.h @@ -85,7 +85,6 @@ Handleset_reset(HandleSet self); void Handleset_addSocket(HandleSet self, const Socket sock); - /** * \brief wait for a socket to become ready * @@ -95,8 +94,11 @@ Handleset_addSocket(HandleSet self, const Socket sock); * data is pending. * The function shall return -1 if a socket error occures. * - * \param self the HandleSet instance - * \param timeout in milliseconds (ms) + * \param self the HandleSet instance + * \param timeout in milliseconds (ms) + * \return It returns the number of sockets on which data is pending + * or 0 if no data is pending on any of the monitored connections. + * The function shall return -1 if a socket error occures. */ int Handleset_waitReady(HandleSet self, unsigned int timeoutMs); diff --git a/lib60870-C/src/common/inc/lib_memory.h b/lib60870-C/src/hal/inc/lib_memory.h similarity index 100% rename from lib60870-C/src/common/inc/lib_memory.h rename to lib60870-C/src/hal/inc/lib_memory.h diff --git a/lib60870-C/src/hal/inc/tls_config.h b/lib60870-C/src/hal/inc/tls_config.h new file mode 100644 index 00000000..065e389c --- /dev/null +++ b/lib60870-C/src/hal/inc/tls_config.h @@ -0,0 +1,88 @@ +/* + * tls_api.h + * + * TLS Configuration API for protocol stacks using TCP/IP + * + * Copyright 2017-2018 MZ Automation GmbH + * + * Abstraction layer for configuration of different TLS implementations + * + */ + +#ifndef SRC_TLS_CONFIG_H_ +#define SRC_TLS_CONFIG_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * \file tls_config.h + * \brief TLS API functions + */ + +/*! \addtogroup hal Platform (Hardware/OS) abstraction layer + * + * @{ + */ + +/** + * @defgroup TLS_CONFIG_API TLS configuration + * + * @{ + */ + +typedef struct sTLSConfiguration* TLSConfiguration; + +TLSConfiguration +TLSConfiguration_create(void); + +/* will be called by stack automatically */ +void +TLSConfiguration_setClientMode(TLSConfiguration self); + +void +TLSConfiguration_setChainValidation(TLSConfiguration self, bool value); + +void +TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value); + +bool +TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen); + +bool +TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename); + +bool +TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword); + +bool +TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword); + +bool +TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); + +bool +TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename); + +bool +TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); + +bool +TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename); + +void +TLSConfiguration_destroy(TLSConfiguration self); + +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_TLS_CONFIG_H_ */ diff --git a/lib60870-C/src/hal/inc/tls_socket.h b/lib60870-C/src/hal/inc/tls_socket.h new file mode 100644 index 00000000..331d5a96 --- /dev/null +++ b/lib60870-C/src/hal/inc/tls_socket.h @@ -0,0 +1,108 @@ +/* + * tls_api.h + * + * TLS socket API for protocol libraries using TCP/IP + * + * Copyright 2017-2018 MZ Automation GmbH + * + * Abstraction layer for different TLS implementations + * + * The implementation has to connect the TLS API layer with the socket API layer + * and perform all TLS tasks like handshake, encryption/decryption. + * + */ + +#ifndef SRC_TLS_SOCKET_API_H_ +#define SRC_TLS_SOCKET_API_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \file tls_socket.h + * \brief Abstraction layer for different TLS implementations. + * + * The implementation has to connect the TLS API layer with the socket API layer + * and perform all TLS tasks like handshake, encryption/decryption. + */ + +/*! \addtogroup hal Platform (Hardware/OS) abstraction layer + * + * @{ + */ + +/** + * @defgroup HAL_TLS_SOCKET Abstraction layer for different TLS implementations. + * + * The implementation has to connect the TLS API layer with the socket API layer + * and perform all TLS tasks like handshake, encryption/decryption. + * + * @{ + */ + +#include +#include "tls_config.h" +#include "hal_socket.h" + +typedef struct sTLSSocket* TLSSocket; + +/** + * \brief This function create a new TLSSocket instance using the given Socket instance + * + * NOTE: This function also has to perform the TLS handshake + * + * \param socket the socket instance to use for the TLS connection + * \param configuration the TLS configuration object to use + * + * \return new TLS connection instance + */ +TLSSocket +TLSSocket_create(Socket socket, TLSConfiguration configuration); + +/** + * \brief read from socket to local buffer (non-blocking) + * + * The function shall return immediately if no data is available. In this case + * the function returns 0. If an error happens the function shall return -1. + * + * Implementation of this function is MANDATORY + * + * NOTE: The behavior of this function changed with version 0.8! + * + * \param self the client, connection or server socket instance + * \param buf the buffer where the read bytes are copied to + * \param size the maximum number of bytes to read (size of the provided buffer) + * + * \return the number of bytes read or -1 if an error occurred + */ +int +TLSSocket_read(TLSSocket self, uint8_t* buf, int size); + +/** + * \brief send a message through the socket + * + * Implementation of this function is MANDATORY + * + * \param self client, connection or server socket instance + * + * \return number of bytes transmitted of -1 in case of an error + */ +int +TLSSocket_write(TLSSocket self, uint8_t* buf, int size); + +/** + * \brief Closes the TLS connection and released all resources + */ +void +TLSSocket_close(TLSSocket self); + +/*! @} */ + +/*! @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_TLS_SOCKET_API_H_ */ diff --git a/lib60870-C/src/common/lib_memory.c b/lib60870-C/src/hal/memory/lib_memory.c similarity index 100% rename from lib60870-C/src/common/lib_memory.c rename to lib60870-C/src/hal/memory/lib_memory.c diff --git a/lib60870-C/src/tls/mbedtls/mbedtls_config.h b/lib60870-C/src/hal/tls/mbedtls/mbedtls_config.h similarity index 100% rename from lib60870-C/src/tls/mbedtls/mbedtls_config.h rename to lib60870-C/src/hal/tls/mbedtls/mbedtls_config.h diff --git a/lib60870-C/src/tls/mbedtls/tls_mbedtls.c b/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c similarity index 99% rename from lib60870-C/src/tls/mbedtls/tls_mbedtls.c rename to lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c index 3b8ea7e7..001c9105 100644 --- a/lib60870-C/src/tls/mbedtls/tls_mbedtls.c +++ b/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c @@ -11,7 +11,7 @@ #include -#include "tls_api.h" +#include "tls_socket.h" #include "hal_thread.h" #include "lib_memory.h" #include "linked_list.h" diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index faf859b8..d2571e58 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -43,7 +43,7 @@ #include "cs101_asdu_internal.h" #if (CONFIG_CS104_SUPPORT_TLS == 1) -#include "tls_api.h" +#include "tls_socket.h" #endif #if ((CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP != 1) && (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP != 1)) diff --git a/lib60870-C/src/inc/api/cs104_connection.h b/lib60870-C/src/inc/api/cs104_connection.h index 587bc594..7415aaa4 100644 --- a/lib60870-C/src/inc/api/cs104_connection.h +++ b/lib60870-C/src/inc/api/cs104_connection.h @@ -24,7 +24,8 @@ #include #include -#include "tls_api.h" + +#include "tls_socket.h" #include "iec60870_master.h" #ifdef __cplusplus diff --git a/lib60870-C/src/inc/api/iec60870_slave.h b/lib60870-C/src/inc/api/iec60870_slave.h index 7cf51ca1..91c00c30 100644 --- a/lib60870-C/src/inc/api/iec60870_slave.h +++ b/lib60870-C/src/inc/api/iec60870_slave.h @@ -25,7 +25,7 @@ #include #include #include "iec60870_common.h" -#include "tls_api.h" +#include "tls_config.h" #ifdef __cplusplus extern "C" { diff --git a/lib60870-C/src/tls/tls_api.h b/lib60870-C/src/tls/tls_api.h deleted file mode 100644 index 82316186..00000000 --- a/lib60870-C/src/tls/tls_api.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * tls_api.h - * - * TLS API for TCP/IP protocol stacks - * - * Copyright 2017 MZ Automation GmbH - * - * Abstraction layer for different TLS implementations - * - * Implementation connects the TLS API layer with the socket API layer - * and performs all TLS tasks like handshake, encryption/decryption. - * - */ - -#ifndef SRC_TLS_TLS_API_H_ -#define SRC_TLS_TLS_API_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/** - * \file tls_api.h - * \brief TLS API functions - */ - -/** - * @defgroup TLS_CONFIG_API TLS configuration - * - * @{ - */ - -typedef struct sTLSConfiguration* TLSConfiguration; - -TLSConfiguration -TLSConfiguration_create(void); - -/* will be called by stack automatically */ -void -TLSConfiguration_setClientMode(TLSConfiguration self); - -void -TLSConfiguration_setChainValidation(TLSConfiguration self, bool value); - -void -TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value); - -bool -TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen); - -bool -TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename); - -bool -TLSConfiguration_setOwnKey(TLSConfiguration self, uint8_t* key, int keyLen, const char* keyPassword); - -bool -TLSConfiguration_setOwnKeyFromFile(TLSConfiguration self, const char* filename, const char* keyPassword); - -bool -TLSConfiguration_addAllowedCertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); - -bool -TLSConfiguration_addAllowedCertificateFromFile(TLSConfiguration self, const char* filename); - -bool -TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certifcate, int certLen); - -bool -TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename); - -void -TLSConfiguration_destroy(TLSConfiguration self); - -/** @} */ - -#include "hal_socket.h" - -typedef struct sTLSSocket* TLSSocket; - -TLSSocket -TLSSocket_create(Socket socket, TLSConfiguration configuration); - -bool -TLSSocket_performHandshake(TLSSocket self); - -/** - * \brief read from socket to local buffer (non-blocking) - * - * The function shall return immediately if no data is available. In this case - * the function returns 0. If an error happens the function shall return -1. - * - * Implementation of this function is MANDATORY - * - * NOTE: The behaviour of this function changed with version 0.8! - * - * \param self the client, connection or server socket instance - * \param buf the buffer where the read bytes are copied to - * \param size the maximum number of bytes to read (size of the provided buffer) - * - * \return the number of bytes read or -1 if an error occurred - */ -int -TLSSocket_read(TLSSocket self, uint8_t* buf, int size); - -/** - * \brief send a message through the socket - * - * Implementation of this function is MANDATORY - * - * \param self client, connection or server socket instance - * - * \return number of bytes transmitted of -1 in case of an error - */ -int -TLSSocket_write(TLSSocket self, uint8_t* buf, int size); - -void -TLSSocket_close(TLSSocket self); - -#ifdef __cplusplus -} -#endif - -#endif /* SRC_TLS_TLS_API_H_ */ From 8aeb1b9d5f717170d49548936fb8d1b68cde1db9 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 9 Aug 2018 16:42:48 +0200 Subject: [PATCH 16/24] - updated cmake files to make compilation of HAL and common code optional --- lib60870-C/CMakeLists.txt | 16 +++++++++++----- lib60870-C/src/CMakeLists.txt | 15 ++++++++++++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib60870-C/CMakeLists.txt b/lib60870-C/CMakeLists.txt index d7802777..78655e42 100644 --- a/lib60870-C/CMakeLists.txt +++ b/lib60870-C/CMakeLists.txt @@ -28,12 +28,19 @@ check_library_exists(rt clock_gettime "time.h" CONFIG_SYSTEM_HAS_CLOCK_GETTIME) include (TestBigEndian) test_big_endian(PLATFORM_IS_BIGENDIAN) +option(BUILD_HAL "Build the platform abstraction layer (HAL)" ON) +option(BUILD_COMMON "Build common code (shared with other libraries - e.g. libiec61850)" ON) + +option(BUILD_EXAMPLES "Build the examples" ON) +option(BUILD_TESTS "Build the tests" ON) + +if(BUILD_HAL) + if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.6.0) set(WITH_MBEDTLS 1) endif(EXISTS ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.6.0) -option(BUILD_EXAMPLES "Build the examples" ON) -option(BUILD_TESTS "Build the tests" ON) +endif(BUILD_HAL) include_directories( ${CMAKE_CURRENT_LIST_DIR}/config @@ -41,12 +48,11 @@ include_directories( ${CMAKE_CURRENT_LIST_DIR}/src/inc/internal ${CMAKE_CURRENT_LIST_DIR}/src/common/inc ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc - ${CMAKE_CURRENT_LIST_DIR}/src/tls ) if(WITH_MBEDTLS) include_directories( - ${CMAKE_CURRENT_LIST_DIR}/src/tls/mbedtls + ${CMAKE_CURRENT_LIST_DIR}/src/hal/tls/mbedtls ${CMAKE_CURRENT_LIST_DIR}/dependencies/mbedtls-2.6.0/include ) @@ -62,6 +68,7 @@ set(API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_time.h ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_thread.h ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/hal_socket.h + ${CMAKE_CURRENT_LIST_DIR}/src/hal/inc/tls_config.h ${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs101_master.h ${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs101_slave.h ${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs104_slave.h @@ -71,7 +78,6 @@ set(API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs101_information_objects.h ${CMAKE_CURRENT_LIST_DIR}/src/inc/api/cs104_connection.h ${CMAKE_CURRENT_LIST_DIR}/src/inc/api/link_layer_parameters.h - ${CMAKE_CURRENT_LIST_DIR}/src/tls/tls_api.h ) diff --git a/lib60870-C/src/CMakeLists.txt b/lib60870-C/src/CMakeLists.txt index fa5ce6db..10c11811 100644 --- a/lib60870-C/src/CMakeLists.txt +++ b/lib60870-C/src/CMakeLists.txt @@ -1,7 +1,5 @@ set (lib_common_SRCS -./common/lib_memory.c -./common/linked_list.c ./iec60870/apl/cpXXtime2a.c ./iec60870/cs101/cs101_asdu.c ./iec60870/cs101/cs101_bcr.c @@ -20,11 +18,18 @@ set (lib_common_SRCS ./iec60870/lib60870_common.c ) +if (BUILD_COMMON) +list (APPEND lib_common_SRCS ./common/linked_list.c) +endif (BUILD_COMMON) + +if (BUILD_HAL) + set (lib_linux_SRCS ./hal/serial/linux/serial_port_linux.c ./hal/socket/linux/socket_linux.c ./hal/thread/linux/thread_linux.c ./hal/time/unix/time.c +./hal/memory/lib_memory.c ) set (lib_windows_SRCS @@ -32,14 +37,18 @@ set (lib_windows_SRCS ./hal/socket/win32/socket_win32.c ./hal/thread/win32/thread_win32.c ./hal/time/win32/time.c +./hal/memory/lib_memory.c ) set (lib_bsd_SRCS ./hal/socket/bsd/socket_bsd.c ./hal/thread/bsd/thread_bsd.c ./hal/time/unix/time.c +./hal/memory/lib_memory.c ) +endif (BUILD_HAL) + IF(WIN32) IF(MSVC) @@ -76,7 +85,7 @@ ENDIF(WIN32) IF(WITH_MBEDTLS) list (APPEND library_SRCS ${tls_SRCS}) -list (APPEND library_SRCS ./tls/mbedtls/tls_mbedtls.c) +list (APPEND library_SRCS ./hal/tls/mbedtls/tls_mbedtls.c) ENDIF(WITH_MBEDTLS) From bbb450d4e2dbf15d3a68a05a82456648a2b6ba2c Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 9 Aug 2018 17:44:04 +0200 Subject: [PATCH 17/24] - imported new TLS socket functions and API documentation from libiec61850 --- lib60870-C/src/hal/inc/tls_config.h | 47 +++++++++++- lib60870-C/src/hal/inc/tls_socket.h | 23 +++++- lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c | 75 +++++++++++++++---- .../src/iec60870/cs104/cs104_connection.c | 2 +- lib60870-C/src/iec60870/cs104/cs104_slave.c | 2 +- 5 files changed, 130 insertions(+), 19 deletions(-) diff --git a/lib60870-C/src/hal/inc/tls_config.h b/lib60870-C/src/hal/inc/tls_config.h index 065e389c..ec70fe9b 100644 --- a/lib60870-C/src/hal/inc/tls_config.h +++ b/lib60870-C/src/hal/inc/tls_config.h @@ -1,5 +1,5 @@ /* - * tls_api.h + * tls_config.h * * TLS Configuration API for protocol stacks using TCP/IP * @@ -37,22 +37,55 @@ extern "C" { typedef struct sTLSConfiguration* TLSConfiguration; +/** + * \brief Create a new \ref TLSConfiguration object to represent TLS configuration and certificates + * + * \return the new TLS configuration + */ TLSConfiguration TLSConfiguration_create(void); -/* will be called by stack automatically */ +/* will be called by stack automatically when appropriate */ void TLSConfiguration_setClientMode(TLSConfiguration self); +/** + * \brief Enables the validation of the certificate trust chain (enabled by default) + * + * \param value true to enable chain validation, false to disable + */ void TLSConfiguration_setChainValidation(TLSConfiguration self, bool value); +/** + * \brief Set if only known certificates are accepted. + * + * If set to true only known certificates are accepted. Connections with unknown certificates + * are rejected even if they are signed by a trusted authority. + * + * \param value true to enable setting, false otherwise + */ void TLSConfiguration_setAllowOnlyKnownCertificates(TLSConfiguration self, bool value); +/** + * \brief Set own certificate (identity) from a byte buffer + * + * \param certificate the certificate buffer + * \param certLen the lenght of the certificate + * + * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format) + */ bool TLSConfiguration_setOwnCertificate(TLSConfiguration self, uint8_t* certificate, int certLen); +/** + * \brief Set own certificate (identity) from a certificate file + * + * \param filename of the certificate file + * + * \return true, when the certificate was set, false otherwise (e.g. unknown certificate format) + */ bool TLSConfiguration_setOwnCertificateFromFile(TLSConfiguration self, const char* filename); @@ -74,6 +107,16 @@ TLSConfiguration_addCACertificate(TLSConfiguration self, uint8_t* certifcate, in bool TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* filename); +/** + * \brief Set the renegotiation timeout. + * + * After the timeout elapsed a TLS session renegotiation has to occur. + * + * \param timeInMs session renegotiation timeout in milliseconds + */ +void +TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs); + void TLSConfiguration_destroy(TLSConfiguration self); diff --git a/lib60870-C/src/hal/inc/tls_socket.h b/lib60870-C/src/hal/inc/tls_socket.h index 331d5a96..70f1a3e9 100644 --- a/lib60870-C/src/hal/inc/tls_socket.h +++ b/lib60870-C/src/hal/inc/tls_socket.h @@ -1,5 +1,5 @@ /* - * tls_api.h + * tls_socket.h * * TLS socket API for protocol libraries using TCP/IP * @@ -54,11 +54,30 @@ typedef struct sTLSSocket* TLSSocket; * * \param socket the socket instance to use for the TLS connection * \param configuration the TLS configuration object to use + * \param storeClientCert if true, the client certificate will be stored + * for later access by \ref TLSSocket_getPeerCertificate * * \return new TLS connection instance */ TLSSocket -TLSSocket_create(Socket socket, TLSConfiguration configuration); +TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert); + + +/** + * \brief Perform a new TLS handshake/session renegotiation + */ +bool +TLSSocket_performHandshake(TLSSocket self); + +/** + * \brief Access the certificate used by the peer + * + * \param[out] certSize the size of the certificate in bytes + * + * \return the certificate byte buffer + */ +uint8_t* +TLSSocket_getPeerCertificate(TLSSocket self, int* certSize); /** * \brief read from socket to local buffer (non-blocking) diff --git a/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c b/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c index 001c9105..2319d647 100644 --- a/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c +++ b/lib60870-C/src/hal/tls/mbedtls/tls_mbedtls.c @@ -49,11 +49,19 @@ struct sTLSConfiguration { bool chainValidation; bool allowOnlyKnownCertificates; + + /* TLS session renegotioation time in milliseconds */ + int renegotiationTimeInMs; }; struct sTLSSocket { mbedtls_ssl_context ssl; Socket socket; + mbedtls_ssl_config conf; + TLSConfiguration tlsConfig; + bool storePeerCert; + uint8_t* peerCert; + int peerCertLength; }; static bool @@ -74,7 +82,7 @@ compareCertificates(mbedtls_x509_crt *crt1, mbedtls_x509_crt *crt2) static int verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth, uint32_t *flags) { - TLSConfiguration self = (TLSConfiguration) parameter; + TLSSocket self = (TLSSocket) parameter; DEBUG_PRINT("TLS", "Verify cert: depth %i\n", certificate_depth); @@ -86,19 +94,19 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth DEBUG_PRINT("TLS", "%s\n", buffer); - if (self->chainValidation == false) { + if (self->tlsConfig->chainValidation == false) { if (certificate_depth != 0) *flags = 0; } if (certificate_depth == 0) { - if (self->allowOnlyKnownCertificates) { + if (self->tlsConfig->allowOnlyKnownCertificates) { DEBUG_PRINT("TLS", "Check against list of allowed certs\n"); bool certMatches = false; - LinkedList certList = LinkedList_getNext(self->allowedCertificates); + LinkedList certList = LinkedList_getNext(self->tlsConfig->allowedCertificates); while (certList) { mbedtls_x509_crt* allowedCert = (mbedtls_x509_crt*) LinkedList_getData(certList); @@ -120,6 +128,18 @@ verifyCertificate (void* parameter, mbedtls_x509_crt *crt, int certificate_depth else return 1; } + + if (self->storePeerCert) { + + if (*flags == 0) { + + self->peerCertLength = crt->raw.len; + self->peerCert = (uint8_t*) GLOBAL_MALLOC(self->peerCertLength); + memcpy(self->peerCert, crt->raw.p, self->peerCertLength); + + } + + } } return 0; @@ -149,7 +169,6 @@ TLSConfiguration_create() mbedtls_ctr_drbg_seed( &(self->ctr_drbg), mbedtls_entropy_func, &(self->entropy), NULL, 0); mbedtls_ssl_conf_rng( &(self->conf), mbedtls_ctr_drbg_random, &(self->ctr_drbg) ); - mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*) self); mbedtls_ssl_conf_authmode(&(self->conf), MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_renegotiation(&(self->conf), MBEDTLS_SSL_RENEGOTIATION_ENABLED); @@ -287,6 +306,12 @@ TLSConfiguration_addCACertificateFromFile(TLSConfiguration self, const char* fil return (ret == 0); } +void +TLSConfiguration_setRenegotiationTime(TLSConfiguration self, int timeInMs) +{ + self->renegotiationTimeInMs = timeInMs; +} + void TLSConfiguration_destroy(TLSConfiguration self) { @@ -316,38 +341,44 @@ readFunction(void* ctx, unsigned char* buf, size_t len) { int ret = Socket_read((Socket) ctx, buf, len); - if ((ret == 0) && (len > 0)) + if ((ret == 0) && (len > 0)) { return MBEDTLS_ERR_SSL_WANT_READ; + } return ret; } TLSSocket -TLSSocket_create(Socket socket, TLSConfiguration configuration) +TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert) { TLSSocket self = (TLSSocket) GLOBAL_CALLOC(1, sizeof(struct sTLSSocket)); if (self != NULL) { self->socket = socket; + self->tlsConfig = configuration; + self->storePeerCert = storeClientCert; + self->peerCert = NULL; + self->peerCertLength = 0; + + memcpy(&(self->conf), &(configuration->conf), sizeof(mbedtls_ssl_config)); + + mbedtls_ssl_conf_verify(&(self->conf), verifyCertificate, (void*) self); int ret; - mbedtls_ssl_conf_ca_chain( &(configuration->conf), &(configuration->cacerts), NULL ); + mbedtls_ssl_conf_ca_chain( &(self->conf), &(configuration->cacerts), NULL ); - ret = mbedtls_ssl_conf_own_cert( &(configuration->conf), &(configuration->ownCertificate), &(configuration->ownKey)); + ret = mbedtls_ssl_conf_own_cert( &(self->conf), &(configuration->ownCertificate), &(configuration->ownKey)); if (ret != 0) DEBUG_PRINT("TLS", "mbedtls_ssl_conf_own_cert returned %d\n", ret); - ret = mbedtls_ssl_setup( &(self->ssl), &(configuration->conf) ); + ret = mbedtls_ssl_setup( &(self->ssl), &(self->conf) ); if (ret != 0) DEBUG_PRINT("TLS", "mbedtls_ssl_setup returned %d\n", ret); - // if (configuration->conf.endpoint == MBEDTLS_SSL_IS_CLIENT) - // mbedtls_ssl_set_hostname(&(self->ssl), "*"); - mbedtls_ssl_set_bio(&(self->ssl), socket, (mbedtls_ssl_send_t*) Socket_write, (mbedtls_ssl_recv_t*) readFunction, NULL); @@ -367,6 +398,23 @@ TLSSocket_create(Socket socket, TLSConfiguration configuration) return self; } +uint8_t* +TLSSocket_getPeerCertificate(TLSSocket self, int* certSize) +{ + if (certSize) + *certSize = self->peerCertLength; + + return self->peerCert; +} + +bool +TLSSocket_performHandshake(TLSSocket self) +{ + if (mbedtls_ssl_renegotiate(&(self->ssl)) == 0) + return true; + else + return false; +} int TLSSocket_read(TLSSocket self, uint8_t* buf, int size) @@ -441,6 +489,7 @@ TLSSocket_close(TLSSocket self) Thread_sleep(10); + mbedtls_ssl_config_free(&(self->conf)); mbedtls_ssl_free(&(self->ssl)); GLOBAL_FREEMEM(self); diff --git a/lib60870-C/src/iec60870/cs104/cs104_connection.c b/lib60870-C/src/iec60870/cs104/cs104_connection.c index 63b37f22..da0b18c1 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_connection.c +++ b/lib60870-C/src/iec60870/cs104/cs104_connection.c @@ -704,7 +704,7 @@ handleConnection(void* parameter) #if (CONFIG_CS104_SUPPORT_TLS == 1) if (self->tlsConfig != NULL) { - self->tlsSocket = TLSSocket_create(self->socket, self->tlsConfig); + self->tlsSocket = TLSSocket_create(self->socket, self->tlsConfig, false); if (self->tlsSocket) self->running = true; diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index d2571e58..348a7f6b 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -2060,7 +2060,7 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu #if (CONFIG_CS104_SUPPORT_TLS == 1) if (slave->tlsConfig != NULL) { - self->tlsSocket = TLSSocket_create(socket, slave->tlsConfig); + self->tlsSocket = TLSSocket_create(socket, slave->tlsConfig, false); if (self->tlsSocket == NULL) { DEBUG_PRINT("Close connection\n"); From d2477b6a14f17f9dea245cd9a410b6d55c7d8ff7 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 9 Aug 2018 17:58:20 +0200 Subject: [PATCH 18/24] - removed wrong comment --- lib60870-C/src/hal/inc/tls_socket.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib60870-C/src/hal/inc/tls_socket.h b/lib60870-C/src/hal/inc/tls_socket.h index 70f1a3e9..553cb82b 100644 --- a/lib60870-C/src/hal/inc/tls_socket.h +++ b/lib60870-C/src/hal/inc/tls_socket.h @@ -3,7 +3,7 @@ * * TLS socket API for protocol libraries using TCP/IP * - * Copyright 2017-2018 MZ Automation GmbH + * Copyright 2017-2018 Michael Zillgith, MZ Automation GmbH * * Abstraction layer for different TLS implementations * @@ -62,7 +62,6 @@ typedef struct sTLSSocket* TLSSocket; TLSSocket TLSSocket_create(Socket socket, TLSConfiguration configuration, bool storeClientCert); - /** * \brief Perform a new TLS handshake/session renegotiation */ @@ -85,10 +84,6 @@ TLSSocket_getPeerCertificate(TLSSocket self, int* certSize); * The function shall return immediately if no data is available. In this case * the function returns 0. If an error happens the function shall return -1. * - * Implementation of this function is MANDATORY - * - * NOTE: The behavior of this function changed with version 0.8! - * * \param self the client, connection or server socket instance * \param buf the buffer where the read bytes are copied to * \param size the maximum number of bytes to read (size of the provided buffer) From fb645bd4bc25ad377686746eaa5464885a8c3f42 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 14 Aug 2018 18:12:06 +0200 Subject: [PATCH 19/24] - CS104 slave: support static memory allocation --- lib60870-C/config/lib60870_config.h | 63 ++- lib60870-C/src/iec60870/cs104/cs104_slave.c | 515 +++++++++++++++----- 2 files changed, 439 insertions(+), 139 deletions(-) diff --git a/lib60870-C/config/lib60870_config.h b/lib60870-C/config/lib60870_config.h index 06bff912..bf8e6afb 100644 --- a/lib60870-C/config/lib60870_config.h +++ b/lib60870-C/config/lib60870_config.h @@ -9,15 +9,49 @@ /* print debugging information with printf if set to 1 */ #define CONFIG_DEBUG_OUTPUT 0 +/** + * Configure CS104 slave to allocate memory from static memory pool (don't use dynamic memory) + * + * Note: This setting will create different memory pools for objects like client connections, + * ASDU queues, and slave instances. The following settings have to be defined also: + * - \ref CONFIG_CS104_SLAVE_POOL_SIZE (number of slave instances) + * - \ref CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE (message queue pool size) + * - \ref CONFIG_CS104_MESSAGE_QUEUE_SIZE (size of ASDU message queue) + * - \ref CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE (size of high priority message queue) + * - \ref CONFIG_CS104_MAX_K_BUFFER_SIZE (size of k-buffer - restricts the maximum value of the k parameter!) + * + */ +#define CONFIG_CS104_SLAVE_POOL 1 + +/** + * Define the number of slave instances that are available in the pool + */ +#define CONFIG_CS104_SLAVE_POOL_SIZE 1 + +/** + * Define the number of message queues in the pool to be shared between different slaves + */ +#define CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE 1 +/** + * Define the default size for the slave (outstation) message queue. This is used also + * to buffer ASDUs in the case when the connection is lost. + * + * For each queued message about 256 bytes of memory are required. + */ +#define CONFIG_CS104_MESSAGE_QUEUE_SIZE 100 /** - * Use static memory for the slave (outstation) message queue. + * This is a connection specific ASDU queue for the slave (outstation). It is used for connection + * specific ASDUs like those that are automatically generated by the stack or created in + * the slave side callback. The messages in the queue are removed when the connection is lost. * - * Note: Use only when statically linking the library. You can only have - * a single slave instance! - * */ -#define CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE 0 + * For each queued message about 256 bytes of memory are required. + */ +#define CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE 20 + + +#define CONFIG_CS104_MAX_K_BUFFER_SIZE 20 /** * Compile the library to use threads. This will require semaphore support @@ -43,22 +77,7 @@ */ #define CONFIG_SLAVE_SEPARATE_CALLBACK_THREAD_QUEUE_SIZE 12 -/** - * Define the default size for the slave (outstation) message queue. This is used also - * to buffer ASDUs in the case when the connection is lost. - * - * For each queued message about 256 bytes of memory are required. - */ -#define CONFIG_SLAVE_ASDU_QUEUE_SIZE 100 -/** - * This is a connection specific ASDU queue for the slave (outstation). It is used for connection - * specific ASDUs like those that are automatically generated by the stack or created in - * the slave side callback. The messages in the queue are removed when the connection is lost. - * - * For each queued message about 256 bytes of memory are required. - */ -#define CONFIG_SLAVE_CONNECTION_ASDU_QUEUE_SIZE 10 /** * Compile library with support for SINGLE_REDUNDANCY_GROUP server mode (only CS104 server) @@ -71,9 +90,9 @@ #define CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP 1 /** - * Set the maximum number of client connections or 0 for no restriction + * Set the maximum number of client connections */ -#define CONFIG_CS104_MAX_CLIENT_CONNECTIONS 0 +#define CONFIG_CS104_MAX_CLIENT_CONNECTIONS 5 /* activate TCP keep alive mechanism. 1 -> activate */ #define CONFIG_ACTIVATE_TCP_KEEPALIVE 0 diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 348a7f6b..07f62814 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -1,5 +1,5 @@ /* - * Copyright 2016, 2017 MZ Automation GmbH + * Copyright 2016-2018 MZ Automation GmbH * * This file is part of lib60870-C * @@ -113,8 +113,8 @@ struct sMessageQueue { int lastMsgIndex; int firstMsgIndex; -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) - struct sASDUQueueEntry asdus [CONFIG_SLAVE_ASDU_QUEUE_SIZE]; +#if (CONFIG_CS104_SLAVE_POOL == 1) + struct sASDUQueueEntry asdus [CONFIG_CS104_MESSAGE_QUEUE_SIZE]; #else ASDUQueueEntry asdus; #endif @@ -126,10 +126,65 @@ struct sMessageQueue { typedef struct sMessageQueue* MessageQueue; +#if (CONFIG_CS104_SLAVE_POOL == 1) + +struct sMessageQueuePool { + struct sMessageQueue msgQueue; + bool used; +}; + +static bool messageQueuePoolInitialized = false; + +static struct sMessageQueuePool msgQueuePool[CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE]; + +static MessageQueue +AllocateMessageQueueMemory(void) +{ + int i; + + if (!messageQueuePoolInitialized) { + for (i = 0; i < CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE; i++) + msgQueuePool[i].used = false; + + messageQueuePoolInitialized = true; + } + + for (i = 0; i < CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE; i++) { + if (msgQueuePool[i].used == false) { + msgQueuePool[i].used = true; + return &(msgQueuePool[i].msgQueue); + } + } + + DEBUG_PRINT("AllocateMessageQueueMemory: failed\n"); + + return NULL; +} + +static void +ReleaseMessageQueueMemory(MessageQueue queue) +{ + int i; + + for (i = 0; i < CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE; i++) { + if (msgQueuePool[i].used == true) { + if (&(msgQueuePool[i].msgQueue) == queue) { + msgQueuePool[i].used = false; + return; + } + } + } + + DEBUG_PRINT("ReleaseMessageQueueMemory: failed\n"); +} + +#endif /* (CONFIG_CS104_SLAVE_POOL == 1) */ + static void MessageQueue_initialize(MessageQueue self, int maxQueueSize) { -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) +#if (CONFIG_CS104_SLAVE_POOL == 1) + memset(self->asdus, 0, sizeof(struct sASDUQueueEntry) * CONFIG_CS104_MESSAGE_QUEUE_SIZE); #else self->asdus = (ASDUQueueEntry) GLOBAL_CALLOC(maxQueueSize, sizeof(struct sASDUQueueEntry)); #endif @@ -137,8 +192,15 @@ MessageQueue_initialize(MessageQueue self, int maxQueueSize) self->entryCounter = 0; self->firstMsgIndex = 0; self->lastMsgIndex = 0; + self->size = maxQueueSize; +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (maxQueueSize > CONFIG_CS104_MESSAGE_QUEUE_SIZE) + self->size = CONFIG_CS104_MESSAGE_QUEUE_SIZE; +#endif + + #if (CONFIG_USE_SEMAPHORES == 1) self->queueLock = Semaphore_create(1); #endif @@ -147,7 +209,11 @@ MessageQueue_initialize(MessageQueue self, int maxQueueSize) static MessageQueue MessageQueue_create(int maxQueueSize) { +#if (CONFIG_CS104_SLAVE_POOL == 1) + MessageQueue self = AllocateMessageQueueMemory(); +#else MessageQueue self = (MessageQueue) GLOBAL_MALLOC(sizeof(struct sMessageQueue)); +#endif if (self != NULL) MessageQueue_initialize(self, maxQueueSize); @@ -159,7 +225,8 @@ static void MessageQueue_destroy(MessageQueue self) { if (self != NULL) { -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE != 1) + +#if (CONFIG_CS104_SLAVE_POOL != 1) GLOBAL_FREEMEM(self->asdus); #endif @@ -167,7 +234,11 @@ MessageQueue_destroy(MessageQueue self) Semaphore_destroy(self->queueLock); #endif +#if (CONFIG_CS104_SLAVE_POOL == 1) + ReleaseMessageQueueMemory(self); +#else GLOBAL_FREEMEM(self); +#endif } } @@ -397,8 +468,8 @@ struct sHighPriorityASDUQueue { int lastMsgIndex; int firstMsgIndex; -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) - FrameBuffer asdus[CONFIG_SLAVE_CONNECTION_ASDU_QUEUE_SIZE]; +#if (CONFIG_CS104_SLAVE_POOL == 1) + FrameBuffer asdus[CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE]; #else FrameBuffer* asdus; #endif @@ -410,20 +481,79 @@ struct sHighPriorityASDUQueue { typedef struct sHighPriorityASDUQueue* HighPriorityASDUQueue; +#if (CONFIG_CS104_SLAVE_POOL == 1) + +struct sHighPrioQueuePool { + struct sHighPriorityASDUQueue msgQueue; + bool used; +}; + +static bool highPrioQueuePoolInitialized = false; + +static struct sHighPrioQueuePool highPrioQueuePool[CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE]; + +static HighPriorityASDUQueue +AllocateHighPrioQueueMemory(void) +{ + int i; + + if (!highPrioQueuePoolInitialized) { + for (i = 0; i < CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE; i++) + highPrioQueuePool[i].used = false; + + highPrioQueuePoolInitialized = true; + } + + for (i = 0; i < CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE; i++) { + if (highPrioQueuePool[i].used == false) { + highPrioQueuePool[i].used = true; + return &(highPrioQueuePool[i].msgQueue); + } + } + + DEBUG_PRINT("AllocateHighPrioQueueMemory: failed\n"); + + return NULL; +} + +static void +ReleaseHighPrioQueueMemory(HighPriorityASDUQueue queue) +{ + int i; + + for (i = 0; i < CONFIG_CS104_MESSAGE_QUEUE_POOL_SIZE; i++) { + if (highPrioQueuePool[i].used == true) { + if (&(highPrioQueuePool[i].msgQueue) == queue) { + highPrioQueuePool[i].used = false; + return; + } + } + } + + DEBUG_PRINT("ReleaseHighPrioQueueMemory: failed\n"); +} + +#endif /* (CONFIG_CS104_SLAVE_POOL == 1) */ static void HighPriorityASDUQueue_initialize(HighPriorityASDUQueue self, int maxQueueSize) { -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) -#else +#if (CONFIG_CS104_SLAVE_POOL != 1) self->asdus = (FrameBuffer*) GLOBAL_CALLOC(maxQueueSize, sizeof(FrameBuffer)); #endif self->entryCounter = 0; self->firstMsgIndex = 0; self->lastMsgIndex = 0; + self->size = maxQueueSize; +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (maxQueueSize > CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE) + self->size = CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE; +#endif + + #if (CONFIG_USE_SEMAPHORES == 1) self->queueLock = Semaphore_create(1); #endif @@ -432,7 +562,11 @@ HighPriorityASDUQueue_initialize(HighPriorityASDUQueue self, int maxQueueSize) static HighPriorityASDUQueue HighPriorityASDUQueue_create(int maxQueueSize) { +#if (CONFIG_CS104_SLAVE_POOL == 1) + HighPriorityASDUQueue self = AllocateHighPrioQueueMemory(); +#else HighPriorityASDUQueue self = (HighPriorityASDUQueue) GLOBAL_MALLOC(sizeof(struct sHighPriorityASDUQueue)); +#endif if (self != NULL) HighPriorityASDUQueue_initialize(self, maxQueueSize); @@ -443,7 +577,7 @@ HighPriorityASDUQueue_create(int maxQueueSize) static void HighPriorityASDUQueue_destroy(HighPriorityASDUQueue self) { -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) +#if (CONFIG_CS104_SLAVE_POOL == 1) #else GLOBAL_FREEMEM(self->asdus); #endif @@ -452,7 +586,11 @@ HighPriorityASDUQueue_destroy(HighPriorityASDUQueue self) Semaphore_destroy(self->queueLock); #endif +#if (CONFIG_CS104_SLAVE_POOL == 1) + ReleaseHighPrioQueueMemory(self); +#else GLOBAL_FREEMEM(self); +#endif } static void @@ -636,8 +774,8 @@ struct sCS104_Slave { int maxHighPrioQueueSize; int openConnections; /**< number of connected clients */ + MasterConnection masterConnections[CONFIG_CS104_MAX_CLIENT_CONNECTIONS]; /**< references to all MasterConnection objects */ - LinkedList masterConnections; /**< references to all MasterConnection objects */ #if (CONFIG_USE_SEMAPHORES == 1) Semaphore openConnectionsLock; #endif @@ -669,11 +807,59 @@ struct sCS104_Slave { ServerSocket serverSocket; }; -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) +#if (CONFIG_CS104_SLAVE_POOL == 1) -static struct sCS104_Slave singleStaticSlaveInstance; +struct sCS104_Slave_PoolEntry { + struct sCS104_Slave slave; + bool used; +}; -#endif +static struct sCS104_Slave_PoolEntry slavePool[CONFIG_CS104_SLAVE_POOL_SIZE]; +static bool slavePoolInitialized = false; + +static CS104_Slave +AllocateSlave(void) +{ + int i; + + if (!slavePoolInitialized) { + + for (i = 0; i < CONFIG_CS104_SLAVE_POOL_SIZE; i++) + slavePool[i].used = false; + + slavePoolInitialized = true; + } + + for (i = 0; i < CONFIG_CS104_SLAVE_POOL_SIZE; i++) { + if (slavePool[i].used == false) { + slavePool[i].used = true; + return &(slavePool[i].slave); + } + } + + DEBUG_PRINT("AllocateSlave: failed\n"); + + return NULL; +} + +static void +ReleaseSlave(CS104_Slave slave) +{ + int i; + + for (i = 0; i < CONFIG_CS104_SLAVE_POOL_SIZE; i++) { + if (slavePool[i].used == true) { + if (&(slavePool[i].slave) == slave) { + slavePool[i].used = false; + return; + } + } + } + + DEBUG_PRINT("ReleaseSlave: failed\n"); +} + +#endif /* (CONFIG_CS104_SLAVE_POOL == 1) */ static uint8_t STARTDT_CON_MSG[] = { 0x68, 0x04, 0x0b, 0x00, 0x00, 0x00 }; @@ -697,27 +883,27 @@ initializeMessageQueues(CS104_Slave self, int lowPrioMaxQueueSize, int highPrioM { /* initialized low priority queue */ -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) - lowPrioMaxQueueSize = CONFIG_SLAVE_ASDU_QUEUE_SIZE; +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (lowPrioMaxQueueSize > CONFIG_CS104_MESSAGE_QUEUE_SIZE) + lowPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_SIZE; #else if (lowPrioMaxQueueSize < 1) - lowPrioMaxQueueSize = CONFIG_SLAVE_ASDU_QUEUE_SIZE; + lowPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_SIZE; #endif self->asduQueue = MessageQueue_create(lowPrioMaxQueueSize); - //TODO support static memory allocation mode /* initialize high priority queue */ -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) - highPrioMaxQueueSize = CONFIG_SLAVE_CONNECTION_ASDU_QUEUE_SIZE; +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (highPrioMaxQueueSize > CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE) + highPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE; #else if (highPrioMaxQueueSize < 1) - highPrioMaxQueueSize = CONFIG_SLAVE_CONNECTION_ASDU_QUEUE_SIZE; + highPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE; #endif self->connectionAsduQueue = HighPriorityASDUQueue_create(highPrioMaxQueueSize); - //TODO support static memory allocation mode } #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ @@ -725,8 +911,8 @@ initializeMessageQueues(CS104_Slave self, int lowPrioMaxQueueSize, int highPrioM static CS104_Slave createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) { -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 1) - CS104_Slave self = &(singleStaticSlaveInstance); +#if (CONFIG_CS104_SLAVE_POOL == 1) + CS104_Slave self = AllocateSlave(); #else CS104_Slave self = (CS104_Slave) GLOBAL_MALLOC(sizeof(struct sCS104_Slave)); #endif @@ -749,7 +935,14 @@ createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) self->maxLowPrioQueueSize = maxLowPrioQueueSize; self->maxHighPrioQueueSize = maxHighPrioQueueSize; - self->masterConnections = LinkedList_create(); + { + int i; + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + self->masterConnections[i] = NULL; + } + } + self->maxOpenConnections = CONFIG_CS104_MAX_CLIENT_CONNECTIONS; #if (CONFIG_USE_SEMAPHORES == 1) self->openConnectionsLock = Semaphore_create(1); @@ -848,6 +1041,28 @@ CS104_Slave_getOpenConnections(CS104_Slave self) return openConnections; } +static void +addOpenConnection(CS104_Slave self, MasterConnection connection) +{ +#if (CONFIG_USE_SEMAPHORES) + Semaphore_wait(self->openConnectionsLock); +#endif + + int i; + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + if (self->masterConnections[i] == NULL) { + self->masterConnections[i] = connection; + self->openConnections++; + break; + } + } + +#if (CONFIG_USE_SEMAPHORES) + Semaphore_post(self->openConnectionsLock); +#endif +} + void CS104_Slave_setMaxOpenConnections(CS104_Slave self, int maxOpenConnections) { @@ -886,17 +1101,16 @@ CS104_Slave_activate(CS104_Slave self, MasterConnection connectionToActivate) #if (CONFIG_USE_SEMAPHORES == 1) Semaphore_wait(self->openConnectionsLock); #endif + int i; - LinkedList element; + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + MasterConnection con = self->masterConnections[i]; - for (element = LinkedList_getNext(self->masterConnections); - element != NULL; - element = LinkedList_getNext(element)) - { - MasterConnection connection = (MasterConnection) LinkedList_getData(element); + if (con) { + if (con!= connectionToActivate) + MasterConnection_deactivate(con); + } - if (connection != connectionToActivate) - MasterConnection_deactivate(connection); } #if (CONFIG_USE_SEMAPHORES == 1) @@ -1006,7 +1220,14 @@ struct sMasterConnection { int maxSentASDUs; int oldestSentASDU; int newestSentASDU; + +#if (CONFIG_CS104_SLAVE_POOL == 1) + SentASDUSlave sentASDUs[CONFIG_CS104_MAX_K_BUFFER_SIZE]; +#else SentASDUSlave* sentASDUs; +#endif + + #if (CONFIG_USE_SEMAPHORES == 1) Semaphore sentASDUsLock; #endif @@ -1020,6 +1241,60 @@ struct sMasterConnection { }; +#if (CONFIG_CS104_SLAVE_POOL == 1) + +struct sMasterConnectionPool { + struct sMasterConnection con; + bool used; +}; + +static bool conPoolInitialized = false; + +static struct sMasterConnectionPool conPool[CONFIG_CS104_MAX_CLIENT_CONNECTIONS * CONFIG_CS104_SLAVE_POOL_SIZE]; + +static MasterConnection +AllocateConnectionMemory(void) +{ + int i; + + if (!conPoolInitialized) { + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS * CONFIG_CS104_SLAVE_POOL_SIZE; i++) + conPool[i].used = false; + + conPoolInitialized = true; + } + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS * CONFIG_CS104_SLAVE_POOL_SIZE; i++) { + if (conPool[i].used == false) { + conPool[i].used = true; + return &(conPool[i].con); + } + } + + DEBUG_PRINT("AllocateConnectionMemory: failed\n"); + + return NULL; +} + +static void +ReleaseConnectionMemory(MasterConnection con) +{ + int i; + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS * CONFIG_CS104_SLAVE_POOL_SIZE; i++) { + if (conPool[i].used == true) { + if (&(conPool[i].con) == con) { + conPool[i].used = false; + return; + } + } + } + + DEBUG_PRINT("ReleaseConnectionMemory: failed\n"); +} + +#endif /* (CONFIG_CS104_SLAVE_POOL == 1) */ + static void printSendBuffer(MasterConnection self) { @@ -1691,7 +1966,9 @@ MasterConnection_destroy(MasterConnection self) Socket_destroy(self->socket); +#if (CONFIG_CS104_SLAVE_POOL != 1) GLOBAL_FREEMEM(self->sentASDUs); +#endif #if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->sentASDUsLock); @@ -1699,7 +1976,11 @@ MasterConnection_destroy(MasterConnection self) Handleset_destroy(self->handleSet); +#if (CONFIG_CS104_SLAVE_POOL == 1) + ReleaseConnectionMemory(self); +#else GLOBAL_FREEMEM(self); +#endif } } @@ -1874,7 +2155,15 @@ CS104_Slave_removeConnection(CS104_Slave self, MasterConnection connection) #endif self->openConnections--; - LinkedList_remove(self->masterConnections, (void*) connection); + + int i; + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + if (self->masterConnections[i] == connection) { + self->masterConnections[i] = NULL; + break; + } + } MasterConnection_destroy(connection); @@ -2025,7 +2314,11 @@ _getApplicationLayerParameters(IMasterConnection self) static MasterConnection MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQueue, HighPriorityASDUQueue highPrioQueue) { +#if (CONFIG_CS104_SLAVE_POOL == 1) + MasterConnection self = AllocateConnectionMemory(); +#else MasterConnection self = (MasterConnection) GLOBAL_MALLOC(sizeof(struct sMasterConnection)); +#endif if (self != NULL) { @@ -2044,7 +2337,15 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu self->maxSentASDUs = slave->conParameters.k; self->oldestSentASDU = -1; self->newestSentASDU = -1; + +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (slave->conParameters.k > CONFIG_CS104_MAX_K_BUFFER_SIZE) { + DEBUG_PRINT("Parameter k is to large!\n"); + self->maxSentASDUs = CONFIG_CS104_MAX_K_BUFFER_SIZE; + } +#else self->sentASDUs = (SentASDUSlave*) GLOBAL_MALLOC(sizeof(SentASDUSlave) * self->maxSentASDUs); +#endif self->iMasterConnection.object = self; self->iMasterConnection.getApplicationLayerParameters = _getApplicationLayerParameters; @@ -2174,74 +2475,72 @@ handleClientConnections(CS104_Slave self) if (self->openConnections > 0) { - LinkedList connection = LinkedList_getNext(self->masterConnections); + int i; bool first = true; - while (connection) { - MasterConnection con = (MasterConnection) LinkedList_getData(connection); + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { - if (con->isRunning) { + MasterConnection con = self->masterConnections[i]; - connection = LinkedList_getNext(connection); + if (con != NULL) { - if (first) { - handleset = con->handleSet; - Handleset_reset(handleset); - first = false; - } + if (con->isRunning) { - Handleset_addSocket(handleset, con->socket); - } - else { + if (first) { - connection = LinkedList_getNext(connection); + handleset = con->handleSet; + Handleset_reset(handleset); + first = false; + } - if (self->connectionEventHandler) { - self->connectionEventHandler(self->connectionEventHandlerParameter, &(con->iMasterConnection), CS104_CON_EVENT_CONNECTION_CLOSED); + Handleset_addSocket(handleset, con->socket); } + else { - DEBUG_PRINT("Connection closed\n"); + if (self->connectionEventHandler) { + self->connectionEventHandler(self->connectionEventHandlerParameter, &(con->iMasterConnection), CS104_CON_EVENT_CONNECTION_CLOSED); + } - LinkedList_remove(self->masterConnections, con); + DEBUG_PRINT("Connection closed\n"); - self->openConnections--; + self->masterConnections[i] = NULL; + + self->openConnections--; + + MasterConnection_destroy(con); + } - MasterConnection_destroy(con); } } + /* handle incoming messages when available */ if (handleset != NULL) { - if (Handleset_waitReady(handleset, 1)) { - connection = LinkedList_getNext(self->masterConnections); - while (connection) { + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + MasterConnection con = self->masterConnections[i]; - MasterConnection con = (MasterConnection) LinkedList_getData(connection); - - MasterConnection_handleTcpConnection(con); - - connection = LinkedList_getNext(connection); + if (con != NULL) + MasterConnection_handleTcpConnection(con); } + } } + /* handle periodic tasks for running connections */ + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + MasterConnection con = self->masterConnections[i]; - connection = LinkedList_getNext(self->masterConnections); - - while (connection) { - - MasterConnection con = (MasterConnection) LinkedList_getData(connection); - - if (con->isRunning) - MasterConnection_executePeriodicTasks(con); - - connection = LinkedList_getNext(connection); + if (con != NULL) { + if (con->isRunning) + MasterConnection_executePeriodicTasks(con); + } } + } } @@ -2298,16 +2597,7 @@ handleConnectionsThreadless(CS104_Slave self) if (connection) { -#if (CONFIG_USE_SEMAPHORES) - Semaphore_wait(self->openConnectionsLock); -#endif - - self->openConnections++; - LinkedList_add(self->masterConnections, connection); - -#if (CONFIG_USE_SEMAPHORES) - Semaphore_post(self->openConnectionsLock); -#endif + addOpenConnection(self, connection); connection->isRunning = true; @@ -2401,16 +2691,7 @@ serverThread (void* parameter) if (connection) { -#if (CONFIG_USE_SEMAPHORES) - Semaphore_wait(self->openConnectionsLock); -#endif - - self->openConnections++; - LinkedList_add(self->masterConnections, connection); - -#if (CONFIG_USE_SEMAPHORES) - Semaphore_post(self->openConnectionsLock); -#endif + addOpenConnection(self, connection); /* now start the connection handling (thread) */ MasterConnection_start(connection); @@ -2456,15 +2737,15 @@ CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu) * Dispatch event to all open client connections ************************************************/ - LinkedList element; + int i; - for (element = LinkedList_getNext(self->masterConnections); - element != NULL; - element = LinkedList_getNext(element)) - { - MasterConnection connection = (MasterConnection) LinkedList_getData(element); + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + + MasterConnection con = self->masterConnections[i]; + + if (con) + MessageQueue_enqueueASDU(con->lowPrioQueue, asdu); - MessageQueue_enqueueASDU(connection->lowPrioQueue, asdu); } #if (CONFIG_USE_SEMAPHORES == 1) @@ -2602,15 +2883,13 @@ CS104_Slave_destroy(CS104_Slave self) Semaphore_wait(self->openConnectionsLock); #endif - LinkedList element; - - for (element = LinkedList_getNext(self->masterConnections); - element != NULL; - element = LinkedList_getNext(element)) { - MasterConnection connection = (MasterConnection) LinkedList_getData(element); + int i; - MasterConnection_close(connection); + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + if (self->masterConnections[i] != NULL) + MasterConnection_close(self->masterConnections[i]); + } } #if (CONFIG_USE_SEMAPHORES == 1) @@ -2620,14 +2899,16 @@ CS104_Slave_destroy(CS104_Slave self) #if (CONFIG_USE_THREADS == 1) if (self->isThreadlessMode) { #endif - for (element = LinkedList_getNext(self->masterConnections); - element != NULL; - element = LinkedList_getNext(element)) - { - MasterConnection connection = (MasterConnection) LinkedList_getData(element); - MasterConnection_destroy(connection); + int i; + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + if (self->masterConnections[i] != NULL) { + MasterConnection_destroy(self->masterConnections[i]); + self->masterConnections[i] = NULL; + } } + #if (CONFIG_USE_THREADS == 1) } else { @@ -2637,21 +2918,21 @@ CS104_Slave_destroy(CS104_Slave self) } #endif - LinkedList_destroyStatic(self->masterConnections); - #if (CONFIG_USE_SEMAPHORES == 1) Semaphore_destroy(self->openConnectionsLock); #endif -#if (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 0) - #if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) { MessageQueue_destroy(self->asduQueue); HighPriorityASDUQueue_destroy(self->connectionAsduQueue); } #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ + +#if (CONFIG_CS104_SLAVE_POOL == 1) + ReleaseSlave(self); +#else GLOBAL_FREEMEM(self); +#endif -#endif /* (CONFIG_SLAVE_WITH_STATIC_MESSAGE_QUEUE == 0) */ } From 39ec8ddd08f86a1a66f1c7796254b3a0c1eaa2a8 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Tue, 14 Aug 2018 21:17:35 +0200 Subject: [PATCH 20/24] - CS104 slave: added static allocation for local IP address --- lib60870-C/config/lib60870_config.h | 11 ++++++++--- lib60870-C/src/iec60870/cs104/cs104_slave.c | 21 +++++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lib60870-C/config/lib60870_config.h b/lib60870-C/config/lib60870_config.h index bf8e6afb..76022bea 100644 --- a/lib60870-C/config/lib60870_config.h +++ b/lib60870-C/config/lib60870_config.h @@ -48,10 +48,15 @@ * * For each queued message about 256 bytes of memory are required. */ -#define CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE 20 +#define CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE 50 - -#define CONFIG_CS104_MAX_K_BUFFER_SIZE 20 +/** + * \brief This parameter restricts the k-buffer size when \ref CONFIG_CS104_SLAVE_POOL is enabled + * + * The actual size of the k-buffer (corresponding to the APCI parameter k) is defined by the runtime parameter. + * However the runtime parameter is restricted to the maximum of this value. + */ +#define CONFIG_CS104_MAX_K_BUFFER_SIZE 30 /** * Compile the library to use threads. This will require semaphore support diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index 07f62814..a36892af 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -786,9 +786,6 @@ struct sCS104_Slave { int maxOpenConnections; /**< maximum accepted open client connections */ - /* TODO if configured for fixed number of connections and connection_is_redundancy_group, - * add connection queues here */ - struct sCS104_APCIParameters conParameters; struct sCS101_AppLayerParameters alParameters; @@ -801,6 +798,10 @@ struct sCS104_Slave { CS104_ServerMode serverMode; +#if (CONFIG_CS104_SLAVE_POOL == 1) + char _localAddress[60]; +#endif + char* localAddress; Thread listeningThread; @@ -1008,6 +1009,15 @@ CS104_Slave_setServerMode(CS104_Slave self, CS104_ServerMode serverMode) void CS104_Slave_setLocalAddress(CS104_Slave self, const char* ipAddress) { +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (ipAddress) { + self->localAddress = self->_localAddress; + strncpy(self->_localAddress, ipAddress, sizeof(self->_localAddress)); + } + else + self->localAddress = NULL; + +#else if (self->localAddress) GLOBAL_FREEMEM(self->localAddress); @@ -1015,6 +1025,7 @@ CS104_Slave_setLocalAddress(CS104_Slave self, const char* ipAddress) if (self->localAddress) strcpy(self->localAddress, ipAddress); +#endif } void @@ -2203,7 +2214,7 @@ connectionHandlingThread(void* parameter) if (isAsduWaiting) socketTimeout = 1; else - socketTimeout = 100; /* TODO replace by configurable parameter */ + socketTimeout = 100; if (Handleset_waitReady(self->handleSet, socketTimeout)) { @@ -2873,8 +2884,10 @@ CS104_Slave_destroy(CS104_Slave self) MessageQueue_releaseAllQueuedASDUs(self->asduQueue); #endif +#if (CONFIG_CS104_SLAVE_POOL != 1) if (self->localAddress != NULL) GLOBAL_FREEMEM(self->localAddress); +#endif /* * Stop all connections From 96c052a86b770edb5671d16998e36478768022b4 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Fri, 24 Aug 2018 18:07:41 +0200 Subject: [PATCH 21/24] - CS 101 unbalanced mode: fixed bug in two byte address handling --- lib60870-C/src/iec60870/link_layer/link_layer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib60870-C/src/iec60870/link_layer/link_layer.c b/lib60870-C/src/iec60870/link_layer/link_layer.c index aa728be5..3903b71b 100644 --- a/lib60870-C/src/iec60870/link_layer/link_layer.c +++ b/lib60870-C/src/iec60870/link_layer/link_layer.c @@ -536,7 +536,7 @@ ParserHeaderSecondaryUnbalanced(void* parameter, uint8_t* msg, int msgSize) address = msg [csStart + 1]; if (addressLength > 1) { - address = msg [csStart + 2] * 0x100; + address += msg [csStart + 2] * 0x100; if (address == 65535) isBroadcast = true; From 3d9a7f0b8fa682858a4f0e9ca852411049c652a9 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Thu, 11 Oct 2018 22:09:02 +0200 Subject: [PATCH 22/24] - linux serial driver: unset ICRNL flag in serial device settings --- lib60870-C/src/hal/serial/linux/serial_port_linux.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib60870-C/src/hal/serial/linux/serial_port_linux.c b/lib60870-C/src/hal/serial/linux/serial_port_linux.c index e6308c05..4cd23dfa 100644 --- a/lib60870-C/src/hal/serial/linux/serial_port_linux.c +++ b/lib60870-C/src/hal/serial/linux/serial_port_linux.c @@ -181,7 +181,7 @@ SerialPort_open(SerialPort self) tios.c_iflag |= INPCK; } - tios.c_iflag &= ~(IXON | IXOFF | IXANY); + tios.c_iflag &= ~(IXON | IXOFF | IXANY | ICRNL); tios.c_iflag |= IGNBRK; /* Set ignore break to allow 0xff characters */ tios.c_iflag |= IGNPAR; tios.c_oflag &=~ OPOST; From 6b52887ff6941b8359118a594525ac7f5859ed35 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Sat, 13 Oct 2018 22:27:41 +0200 Subject: [PATCH 23/24] - CS104 server: added support for multiple redundancy groups mode --- lib60870-C/config/lib60870_config.h | 7 +- lib60870-C/examples/CMakeLists.txt | 1 + .../cs104_redundancy_server/CMakeLists.txt | 20 + .../examples/cs104_redundancy_server/Makefile | 20 + .../cs104_redundancy_server.c | 296 ++++++++ lib60870-C/src/iec60870/cs104/cs104_slave.c | 660 +++++++++++++++--- lib60870-C/src/inc/api/cs104_slave.h | 88 ++- lib60870-C/tests/all_tests.c | 115 +++ 8 files changed, 1106 insertions(+), 101 deletions(-) create mode 100644 lib60870-C/examples/cs104_redundancy_server/CMakeLists.txt create mode 100644 lib60870-C/examples/cs104_redundancy_server/Makefile create mode 100644 lib60870-C/examples/cs104_redundancy_server/cs104_redundancy_server.c diff --git a/lib60870-C/config/lib60870_config.h b/lib60870-C/config/lib60870_config.h index 76022bea..89830984 100644 --- a/lib60870-C/config/lib60870_config.h +++ b/lib60870-C/config/lib60870_config.h @@ -21,7 +21,7 @@ * - \ref CONFIG_CS104_MAX_K_BUFFER_SIZE (size of k-buffer - restricts the maximum value of the k parameter!) * */ -#define CONFIG_CS104_SLAVE_POOL 1 +#define CONFIG_CS104_SLAVE_POOL 0 /** * Define the number of slave instances that are available in the pool @@ -89,6 +89,11 @@ */ #define CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP 1 +/** + * Compile library with support for MULTIPLE_REDUNDANCY_GROUPS server mode (only CS104 server) + */ +#define CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS 1 + /** * Compile library with support for CONNECTION_IS_REDUNDANCY_GROUP server mode (only CS104 server) */ diff --git a/lib60870-C/examples/CMakeLists.txt b/lib60870-C/examples/CMakeLists.txt index 73bc3702..c00959a1 100644 --- a/lib60870-C/examples/CMakeLists.txt +++ b/lib60870-C/examples/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(cs101_slave) add_subdirectory(cs104_client) add_subdirectory(cs104_server) add_subdirectory(cs104_server_no_threads) +add_subdirectory(cs104_redundancy_server) add_subdirectory(multi_client_server) if (WITH_MBEDTLS) diff --git a/lib60870-C/examples/cs104_redundancy_server/CMakeLists.txt b/lib60870-C/examples/cs104_redundancy_server/CMakeLists.txt new file mode 100644 index 00000000..0a6636c2 --- /dev/null +++ b/lib60870-C/examples/cs104_redundancy_server/CMakeLists.txt @@ -0,0 +1,20 @@ +include_directories( + . +) + +set(example_SRCS + cs104_redundancy_server.c +) + +IF(WIN32) +set_source_files_properties(${example_SRCS} + PROPERTIES LANGUAGE CXX) +ENDIF(WIN32) + +add_executable(cs104_redundancy_server + ${example_SRCS} +) + +target_link_libraries(cs104_redundancy_server + lib60870 +) diff --git a/lib60870-C/examples/cs104_redundancy_server/Makefile b/lib60870-C/examples/cs104_redundancy_server/Makefile new file mode 100644 index 00000000..35deb471 --- /dev/null +++ b/lib60870-C/examples/cs104_redundancy_server/Makefile @@ -0,0 +1,20 @@ +LIB60870_HOME=../.. + +PROJECT_BINARY_NAME = cs104_redundancy_server +PROJECT_SOURCES = cs104_redundancy_server.c + +include $(LIB60870_HOME)/make/target_system.mk +include $(LIB60870_HOME)/make/stack_includes.mk + +all: $(PROJECT_BINARY_NAME) + +include $(LIB60870_HOME)/make/common_targets.mk + + +$(PROJECT_BINARY_NAME): $(PROJECT_SOURCES) $(LIB_NAME) + $(CC) $(CFLAGS) $(LDFLAGS) -g -o $(PROJECT_BINARY_NAME) $(PROJECT_SOURCES) $(INCLUDES) $(LIB_NAME) $(LDLIBS) + +clean: + rm -f $(PROJECT_BINARY_NAME) + + diff --git a/lib60870-C/examples/cs104_redundancy_server/cs104_redundancy_server.c b/lib60870-C/examples/cs104_redundancy_server/cs104_redundancy_server.c new file mode 100644 index 00000000..fd53684f --- /dev/null +++ b/lib60870-C/examples/cs104_redundancy_server/cs104_redundancy_server.c @@ -0,0 +1,296 @@ +#include +#include +#include +#include +#include + +#include "cs104_slave.h" + +#include "hal_thread.h" +#include "hal_time.h" + +static bool running = true; + +void +sigint_handler(int signalId) +{ + running = false; +} + +void +printCP56Time2a(CP56Time2a time) +{ + printf("%02i:%02i:%02i %02i/%02i/%04i", CP56Time2a_getHour(time), + CP56Time2a_getMinute(time), + CP56Time2a_getSecond(time), + CP56Time2a_getDayOfMonth(time), + CP56Time2a_getMonth(time), + CP56Time2a_getYear(time) + 2000); +} + +/* Callback handler to log sent or received messages (optional) */ +static void +rawMessageHandler(void* parameter, IMasterConnection conneciton, uint8_t* msg, int msgSize, bool sent) +{ + if (sent) + printf("SEND: "); + else + printf("RCVD: "); + + int i; + for (i = 0; i < msgSize; i++) { + printf("%02x ", msg[i]); + } + + printf("\n"); +} + +static bool +clockSyncHandler (void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime) +{ + printf("Process time sync command with time "); printCP56Time2a(newTime); printf("\n"); + + uint64_t newSystemTimeInMs = CP56Time2a_toMsTimestamp(newTime); + + /* Set time for ACT_CON message */ + CP56Time2a_setFromMsTimestamp(newTime, Hal_getTimeInMs()); + + /* update system time here */ + + return true; +} + +static bool +interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi) +{ + printf("Received interrogation for group %i\n", qoi); + + if (qoi == 20) { /* only handle station interrogation */ + + CS101_AppLayerParameters alParams = IMasterConnection_getApplicationLayerParameters(connection); + + IMasterConnection_sendACT_CON(connection, asdu, false); + + /* The CS101 specification only allows information objects without timestamp in GI responses */ + + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, + 0, 1, false, false); + + InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 100, -1, IEC60870_QUALITY_GOOD); + + CS101_ASDU_addInformationObject(newAsdu, io); + + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + MeasuredValueScaled_create((MeasuredValueScaled) io, 101, 23, IEC60870_QUALITY_GOOD)); + + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + MeasuredValueScaled_create((MeasuredValueScaled) io, 102, 2300, IEC60870_QUALITY_GOOD)); + + InformationObject_destroy(io); + + IMasterConnection_sendASDU(connection, newAsdu); + + CS101_ASDU_destroy(newAsdu); + + newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_INTERROGATED_BY_STATION, + 0, 1, false, false); + + io = (InformationObject) SinglePointInformation_create(NULL, 104, true, IEC60870_QUALITY_GOOD); + + CS101_ASDU_addInformationObject(newAsdu, io); + + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) + SinglePointInformation_create((SinglePointInformation) io, 105, false, IEC60870_QUALITY_GOOD)); + + InformationObject_destroy(io); + + IMasterConnection_sendASDU(connection, newAsdu); + + CS101_ASDU_destroy(newAsdu); + + newAsdu = CS101_ASDU_create(alParams, true, CS101_COT_INTERROGATED_BY_STATION, + 0, 1, false, false); + + CS101_ASDU_addInformationObject(newAsdu, io = (InformationObject) SinglePointInformation_create(NULL, 300, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 301, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 302, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 303, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 304, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 305, false, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 306, true, IEC60870_QUALITY_GOOD)); + CS101_ASDU_addInformationObject(newAsdu, (InformationObject) SinglePointInformation_create((SinglePointInformation) io, 307, false, IEC60870_QUALITY_GOOD)); + + InformationObject_destroy(io); + + IMasterConnection_sendASDU(connection, newAsdu); + + CS101_ASDU_destroy(newAsdu); + + IMasterConnection_sendACT_TERM(connection, asdu); + } + else { + IMasterConnection_sendACT_CON(connection, asdu, true); + } + + return true; +} + +static bool +asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu) +{ + if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) { + printf("received single command\n"); + + if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) { + InformationObject io = CS101_ASDU_getElement(asdu, 0); + + if (InformationObject_getObjectAddress(io) == 5000) { + SingleCommand sc = (SingleCommand) io; + + printf("IOA: %i switch to %i\n", InformationObject_getObjectAddress(io), + SingleCommand_getState(sc)); + + CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON); + } + else + CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA); + + InformationObject_destroy(io); + } + else + CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT); + + IMasterConnection_sendASDU(connection, asdu); + + return true; + } + + return false; +} + +static bool +connectionRequestHandler(void* parameter, const char* ipAddress) +{ + printf("New connection request from %s\n", ipAddress); + +#if 0 + if (strcmp(ipAddress, "127.0.0.1") == 0) { + printf("Accept connection\n"); + return true; + } + else { + printf("Deny connection\n"); + return false; + } +#else + return true; +#endif +} + +static void +connectionEventHandler(void* parameter, IMasterConnection con, CS104_PeerConnectionEvent event) +{ + if (event == CS104_CON_EVENT_CONNECTION_OPENED) { + printf("Connection opened (%p)\n", con); + } + else if (event == CS104_CON_EVENT_CONNECTION_CLOSED) { + printf("Connection closed (%p)\n", con); + } + else if (event == CS104_CON_EVENT_ACTIVATED) { + printf("Connection activated (%p)\n", con); + } + else if (event == CS104_CON_EVENT_DEACTIVATED) { + printf("Connection deactivated (%p)\n", con); + } +} + +int +main(int argc, char** argv) +{ + /* Add Ctrl-C handler */ + signal(SIGINT, sigint_handler); + + /* create a new slave/server instance with default connection parameters and + * default message queue size */ + CS104_Slave slave = CS104_Slave_create(100, 100); + + CS104_Slave_setLocalAddress(slave, "0.0.0.0"); + + /* Set mode to a multiple redundancy groups + * NOTE: library has to be compiled with CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP enabled (=1) + */ + CS104_Slave_setServerMode(slave, CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS); + + CS104_RedundancyGroup redGroup1 = CS104_RedundancyGroup_create("red-group-1"); + CS104_RedundancyGroup_addAllowedClient(redGroup1, "192.168.2.9"); + + CS104_RedundancyGroup redGroup2 = CS104_RedundancyGroup_create("red-group-2"); + CS104_RedundancyGroup_addAllowedClient(redGroup2, "192.168.2.223"); + CS104_RedundancyGroup_addAllowedClient(redGroup2, "192.168.2.222"); + + CS104_RedundancyGroup redGroup3 = CS104_RedundancyGroup_create("catch-all"); + + CS104_Slave_addRedundancyGroup(slave, redGroup1); + CS104_Slave_addRedundancyGroup(slave, redGroup2); + CS104_Slave_addRedundancyGroup(slave, redGroup3); + + /* get the connection parameters - we need them to create correct ASDUs */ + CS101_AppLayerParameters alParams = CS104_Slave_getAppLayerParameters(slave); + + /* set the callback handler for the clock synchronization command */ + CS104_Slave_setClockSyncHandler(slave, clockSyncHandler, NULL); + + /* set the callback handler for the interrogation command */ + CS104_Slave_setInterrogationHandler(slave, interrogationHandler, NULL); + + /* set handler for other message types */ + CS104_Slave_setASDUHandler(slave, asduHandler, NULL); + + /* set handler to handle connection requests (optional) */ + CS104_Slave_setConnectionRequestHandler(slave, connectionRequestHandler, NULL); + + /* set handler to track connection events (optional) */ + CS104_Slave_setConnectionEventHandler(slave, connectionEventHandler, NULL); + + /* uncomment to log messages */ + //CS104_Slave_setRawMessageHandler(slave, rawMessageHandler, NULL); + + CS104_Slave_start(slave); + + if (CS104_Slave_isRunning(slave) == false) { + printf("Starting server failed!\n"); + goto exit_program; + } + + int16_t scaledValue = 0; + + while (running) { + + Thread_sleep(1000); + + CS101_ASDU newAsdu = CS101_ASDU_create(alParams, false, CS101_COT_PERIODIC, 0, 1, false, false); + + InformationObject io = (InformationObject) MeasuredValueScaled_create(NULL, 110, scaledValue, IEC60870_QUALITY_GOOD); + + scaledValue++; + + CS101_ASDU_addInformationObject(newAsdu, io); + + InformationObject_destroy(io); + + /* Add ASDU to slave event queue - don't release the ASDU afterwards! + * The ASDU will be released by the Slave instance when the ASDU + * has been sent. + */ + CS104_Slave_enqueueASDU(slave, newAsdu); + + CS101_ASDU_destroy(newAsdu); + } + + CS104_Slave_stop(slave); + +exit_program: + CS104_Slave_destroy(slave); + + Thread_sleep(500); +} diff --git a/lib60870-C/src/iec60870/cs104/cs104_slave.c b/lib60870-C/src/iec60870/cs104/cs104_slave.c index a36892af..38a3f117 100644 --- a/lib60870-C/src/iec60870/cs104/cs104_slave.c +++ b/lib60870-C/src/iec60870/cs104/cs104_slave.c @@ -46,8 +46,8 @@ #include "tls_socket.h" #endif -#if ((CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP != 1) && (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP != 1)) -#error Illegal configuration: Define either CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP or CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP +#if ((CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP != 1) && (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP != 1) && (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS != 1)) +#error Illegal configuration: Define either CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP or CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP or CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS #endif typedef struct sMasterConnection* MasterConnection; @@ -724,6 +724,223 @@ HighPriorityASDUQueue_resetConnectionQueue(HighPriorityASDUQueue self) #endif } +/*************************************************** + * RedundancyGroup + ***************************************************/ + +typedef struct sCS104_IPAddress* CS104_IPAddress; + +struct sCS104_IPAddress +{ + uint8_t address[16]; + eCS104_IPAddressType type; +}; + +static void +CS104_IPAddress_setFromString(CS104_IPAddress self, const char* ipAddrStr) +{ + if (strchr(ipAddrStr, '.') != NULL) { + /* parse IPv4 string */ + self->type = IP_ADDRESS_TYPE_IPV4; + + int i; + + for (i = 0; i < 4; i++) { + self->address[i] = strtoul(ipAddrStr, NULL, 10); + + ipAddrStr = strchr(ipAddrStr, '.'); + + if ((ipAddrStr == NULL) || (*ipAddrStr == 0)) + break; + + ipAddrStr++; + } + } + else { + self->type = IP_ADDRESS_TYPE_IPV6; + + int i; + + for (i = 0; i < 8; i++) { + uint32_t val = strtoul(ipAddrStr, NULL, 16); + + self->address[i * 2] = val / 0x100; + self->address[i * 2 + 1] = val % 0x100; + + ipAddrStr = strchr(ipAddrStr, ':'); + + if ((ipAddrStr == NULL) || (*ipAddrStr == 0)) + break; + + ipAddrStr++; + } + } +} + +static bool +CS104_IPAddress_equals(CS104_IPAddress self, CS104_IPAddress other) +{ + if (self->type != other->type) + return false; + + int size; + + if (self->type == IP_ADDRESS_TYPE_IPV4) + size = 4; + else + size = 16; + + int i; + + for (i = 0; i < size; i++) { + if (self->address[i] != other->address[i]) + return false; + } + + return true; +} + +struct sCS104_RedundancyGroup { + + char* name; /**< name of the group to be shown in debug messages, or NULL */ + + MessageQueue asduQueue; /**< low priority ASDU queue and buffer */ + HighPriorityASDUQueue connectionAsduQueue; /**< high priority ASDU queue */ + + LinkedList allowedClients; +}; + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) +static void +CS104_RedundancyGroup_initializeMessageQueues(CS104_RedundancyGroup self, int lowPrioMaxQueueSize, int highPrioMaxQueueSize) +{ + /* initialized low priority queue */ + +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (lowPrioMaxQueueSize > CONFIG_CS104_MESSAGE_QUEUE_SIZE) + lowPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_SIZE; +#else + if (lowPrioMaxQueueSize < 1) + lowPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_SIZE; +#endif + + self->asduQueue = MessageQueue_create(lowPrioMaxQueueSize); + + /* initialize high priority queue */ + +#if (CONFIG_CS104_SLAVE_POOL == 1) + if (highPrioMaxQueueSize > CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE) + highPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE; +#else + if (highPrioMaxQueueSize < 1) + highPrioMaxQueueSize = CONFIG_CS104_MESSAGE_QUEUE_HIGH_PRIO_SIZE; +#endif + + self->connectionAsduQueue = HighPriorityASDUQueue_create(highPrioMaxQueueSize); +} +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ + +CS104_RedundancyGroup +CS104_RedundancyGroup_create(const char* name) +{ + CS104_RedundancyGroup self = (CS104_RedundancyGroup) GLOBAL_MALLOC(sizeof(struct sCS104_RedundancyGroup)); + + if (self) { + if (name) + self->name = strdup(name); + else + self->name = NULL; + + self->asduQueue = NULL; + self->connectionAsduQueue = NULL; + + self->allowedClients = NULL; + } + + return self; +} + +static void +CS104_RedundancyGroup_destroy(CS104_RedundancyGroup self) +{ + if (self) { + if (self->name) + GLOBAL_FREEMEM(self->name); + + MessageQueue_destroy(self->asduQueue); + HighPriorityASDUQueue_destroy(self->connectionAsduQueue); + + if (self->allowedClients) + LinkedList_destroy(self->allowedClients); + + GLOBAL_FREEMEM(self); + } +} + +void +CS104_RedundancyGroup_addAllowedClient(CS104_RedundancyGroup self, const char* ipAddress) +{ + struct sCS104_IPAddress ipAddr; + + CS104_IPAddress_setFromString(&ipAddr, ipAddress); + + CS104_RedundancyGroup_addAllowedClientEx(self, ipAddr.address, ipAddr.type); +} + +void +CS104_RedundancyGroup_addAllowedClientEx(CS104_RedundancyGroup self, uint8_t* ipAddress, eCS104_IPAddressType addressType) +{ + if (self->allowedClients == NULL) + self->allowedClients = LinkedList_create(); + + CS104_IPAddress ipAddr = (CS104_IPAddress) GLOBAL_MALLOC(sizeof(struct sCS104_IPAddress)); + + ipAddr->type = addressType; + + int size; + + if (addressType == IP_ADDRESS_TYPE_IPV4) + size = 4; + else + size = 16; + + int i; + + for (i = 0; i < size; i++) + ipAddr->address[i] = ipAddress[i]; + + LinkedList_add(self->allowedClients, ipAddr); +} + +static bool +CS104_RedundancyGroup_matches(CS104_RedundancyGroup self, CS104_IPAddress ipAddress) +{ + if (self->allowedClients == NULL) + return false; + + LinkedList element = LinkedList_getNext(self->allowedClients); + + while (element) { + + CS104_IPAddress allowedAddress = (CS104_IPAddress) LinkedList_getData(element); + + if (CS104_IPAddress_equals(ipAddress, allowedAddress)) + return true; + + element = LinkedList_getNext(element); + } + + return false; +} + +static bool +CS104_RedundancyGroup_isCatchAll(CS104_RedundancyGroup self) +{ + if (self->allowedClients) + return false; + else + return true; +} /*************************************************** @@ -796,6 +1013,10 @@ struct sCS104_Slave { int tcpPort; +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS) + LinkedList redundancyGroups; +#endif + CS104_ServerMode serverMode; #if (CONFIG_CS104_SLAVE_POOL == 1) @@ -808,6 +1029,69 @@ struct sCS104_Slave { ServerSocket serverSocket; }; +typedef struct { + /* required to identify message in server (low-priority) queue */ + uint64_t entryTime; + int queueIndex; /* -1 if ASDU is not from low-priority queue */ + + /* required for T1 timeout */ + uint64_t sentTime; + int seqNo; +} SentASDUSlave; + +struct sMasterConnection { + + Socket socket; + +#if (CONFIG_CS104_SUPPORT_TLS == 1) + TLSSocket tlsSocket; +#endif + + struct sIMasterConnection iMasterConnection; + + CS104_Slave slave; + bool isActive; + bool isRunning; + + int sendCount; /* sent messages - sequence counter */ + int receiveCount; /* received messages - sequence counter */ + + int unconfirmedReceivedIMessages; /* number of unconfirmed messages received */ + + /* timeout T2 handling */ + uint64_t lastConfirmationTime; /* timestamp when the last confirmation message (for I messages) was sent */ + bool timeoutT2Triggered; + + uint64_t nextT3Timeout; + int outstandingTestFRConMessages; + + int maxSentASDUs; + int oldestSentASDU; + int newestSentASDU; + +#if (CONFIG_CS104_SLAVE_POOL == 1) + SentASDUSlave sentASDUs[CONFIG_CS104_MAX_K_BUFFER_SIZE]; +#else + SentASDUSlave* sentASDUs; +#endif + + +#if (CONFIG_USE_SEMAPHORES == 1) + Semaphore sentASDUsLock; +#endif + + HandleSet handleSet; + + uint8_t buffer[260]; + + MessageQueue lowPrioQueue; + HighPriorityASDUQueue highPrioQueue; + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + CS104_RedundancyGroup redundancyGroup; +#endif +}; + #if (CONFIG_CS104_SLAVE_POOL == 1) struct sCS104_Slave_PoolEntry { @@ -908,7 +1192,6 @@ initializeMessageQueues(CS104_Slave self, int lowPrioMaxQueueSize, int highPrioM } #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ - static CS104_Slave createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) { @@ -967,6 +1250,10 @@ createSlave(int maxLowPrioQueueSize, int maxHighPrioQueueSize) self->tlsConfig = NULL; #endif +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + self->redundancyGroups = NULL; +#endif + #if (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) self->serverMode = CS104_MODE_SINGLE_REDUNDANCY_GROUP; #else @@ -1118,7 +1405,7 @@ CS104_Slave_activate(CS104_Slave self, MasterConnection connectionToActivate) MasterConnection con = self->masterConnections[i]; if (con) { - if (con!= connectionToActivate) + if (con != connectionToActivate) MasterConnection_deactivate(con); } @@ -1131,6 +1418,37 @@ CS104_Slave_activate(CS104_Slave self, MasterConnection connectionToActivate) } #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) { + + /* Deactivate all other connections of the same redundancy group */ +#if (CONFIG_USE_SEMAPHORES == 1) + Semaphore_wait(self->openConnectionsLock); +#endif + + int i; + + for (i = 0; i < CONFIG_CS104_MAX_CLIENT_CONNECTIONS; i++) { + MasterConnection con = self->masterConnections[i]; + + if (con) { + if (con->redundancyGroup == connectionToActivate->redundancyGroup) { + if (con != connectionToActivate) + MasterConnection_deactivate(con); + } + } + + } + +#if (CONFIG_USE_SEMAPHORES == 1) + Semaphore_post(self->openConnectionsLock); +#endif + + } + +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ + MasterConnection_activate(connectionToActivate); } @@ -1192,66 +1510,6 @@ CS104_Slave_getAppLayerParameters(CS104_Slave self) * MasterConnection *********************************************************/ -typedef struct { - /* required to identify message in server (low-priority) queue */ - uint64_t entryTime; - int queueIndex; /* -1 if ASDU is not from low-priority queue */ - - /* required for T1 timeout */ - uint64_t sentTime; - int seqNo; -} SentASDUSlave; - -struct sMasterConnection { - - Socket socket; - -#if (CONFIG_CS104_SUPPORT_TLS == 1) - TLSSocket tlsSocket; -#endif - - struct sIMasterConnection iMasterConnection; - - CS104_Slave slave; - bool isActive; - bool isRunning; - - int sendCount; /* sent messages - sequence counter */ - int receiveCount; /* received messages - sequence counter */ - - int unconfirmedReceivedIMessages; /* number of unconfirmed messages received */ - - /* timeout T2 handling */ - uint64_t lastConfirmationTime; /* timestamp when the last confirmation message (for I messages) was sent */ - bool timeoutT2Triggered; - - uint64_t nextT3Timeout; - int outstandingTestFRConMessages; - - int maxSentASDUs; - int oldestSentASDU; - int newestSentASDU; - -#if (CONFIG_CS104_SLAVE_POOL == 1) - SentASDUSlave sentASDUs[CONFIG_CS104_MAX_K_BUFFER_SIZE]; -#else - SentASDUSlave* sentASDUs; -#endif - - -#if (CONFIG_USE_SEMAPHORES == 1) - Semaphore sentASDUsLock; -#endif - - HandleSet handleSet; - - uint8_t buffer[260]; - - MessageQueue lowPrioQueue; - HighPriorityASDUQueue highPrioQueue; -}; - - #if (CONFIG_CS104_SLAVE_POOL == 1) struct sMasterConnectionPool { @@ -2396,6 +2654,20 @@ MasterConnection_create(CS104_Slave slave, Socket socket, MessageQueue lowPrioQu return self; } +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) +static MasterConnection +MasterConnection_createEx(CS104_Slave slave, Socket socket, CS104_RedundancyGroup redGroup) +{ + MasterConnection self = MasterConnection_create(slave, socket, redGroup->asduQueue, redGroup->connectionAsduQueue); + + if (self) + self->redundancyGroup = redGroup; + + return self; +} +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ + + static void MasterConnection_start(MasterConnection self) { @@ -2556,12 +2828,88 @@ handleClientConnections(CS104_Slave self) } +static char* +getPeerAddress(Socket socket, char* ipAddress) +{ + char* ipAddrStr; + + Socket_getPeerAddressStatic(socket, ipAddress); + + /* remove TCP port part */ + if (ipAddress[0] == '[') { + /* IPV6 address */ + ipAddrStr = ipAddress + 1; + + char* separator = strchr(ipAddrStr, ']'); + + if (separator != NULL) + *separator = 0; + + } + else { + /* IPV4 address */ + ipAddrStr = ipAddress; + + char* separator = strchr(ipAddrStr, ':'); + + if (separator != NULL) + *separator = 0; + } + + return ipAddrStr; +} + +static bool +callConnectionRequestHandler(CS104_Slave self, Socket newSocket) +{ + if (self->connectionRequestHandler != NULL) { + char ipAddress[60]; + + char* ipAddrStr = getPeerAddress(newSocket, ipAddress); + + return self->connectionRequestHandler(self->connectionRequestHandlerParameter, + ipAddrStr); + } + else + return true; +} + +static CS104_RedundancyGroup +getMatchingRedundancyGroup(CS104_Slave self, char* ipAddrStr) +{ + struct sCS104_IPAddress ipAddress; + + CS104_IPAddress_setFromString(&ipAddress, ipAddrStr); + + CS104_RedundancyGroup catchAllGroup = NULL; + CS104_RedundancyGroup matchingGroup = NULL; + + LinkedList element = LinkedList_getNext(self->redundancyGroups); + + while (element) { + CS104_RedundancyGroup redGroup = (CS104_RedundancyGroup) LinkedList_getData(element); + + if (CS104_RedundancyGroup_matches(redGroup, &ipAddress)) { + matchingGroup = redGroup; + break; + } + + if (CS104_RedundancyGroup_isCatchAll(redGroup)) + catchAllGroup = redGroup; + + element = LinkedList_getNext(element); + } + + if (matchingGroup == NULL) + matchingGroup = catchAllGroup; + + return matchingGroup; +} + /* handle TCP connections in non-threaded mode */ static void handleConnectionsThreadless(CS104_Slave self) { - - if ((self->maxOpenConnections < 1) || (self->openConnections < self->maxOpenConnections)) { Socket newSocket = ServerSocket_accept(self->serverSocket); @@ -2570,19 +2918,8 @@ handleConnectionsThreadless(CS104_Slave self) bool acceptConnection = true; - if (acceptConnection && (self->connectionRequestHandler != NULL)) { - char ipAddress[60]; - - Socket_getPeerAddressStatic(newSocket, ipAddress); - - /* remove TCP port part */ - char* separator = strchr(ipAddress, ':'); - if (separator != NULL) - *separator = 0; - - acceptConnection = self->connectionRequestHandler(self->connectionRequestHandlerParameter, - ipAddress); - } + if (acceptConnection) + acceptConnection = callConnectionRequestHandler(self, newSocket); if (acceptConnection) { @@ -2602,9 +2939,36 @@ handleConnectionsThreadless(CS104_Slave self) highPrioQueue = HighPriorityASDUQueue_create(self->maxHighPrioQueueSize); } #endif + MasterConnection connection = NULL; - MasterConnection connection = - MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) { + + char ipAddress[60]; + + char* ipAddrStr = getPeerAddress(newSocket, ipAddress); + + CS104_RedundancyGroup matchingGroup = getMatchingRedundancyGroup(self, ipAddrStr); + + if (matchingGroup != NULL) { + connection = MasterConnection_createEx(self, newSocket, matchingGroup); + + if (matchingGroup->name) { + DEBUG_PRINT("Add connection to group: %s\n", matchingGroup->name); + } + } + else { + DEBUG_PRINT("Found no matching redundancy group -> close connection\n"); + } + + + } + else { + connection = MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); + } +#else + connection = MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); +#endif if (connection) { @@ -2616,8 +2980,10 @@ handleConnectionsThreadless(CS104_Slave self) self->connectionEventHandler(self->connectionEventHandlerParameter, &(connection->iMasterConnection), CS104_CON_EVENT_CONNECTION_OPENED); } } - else - DEBUG_PRINT("Connection attempt failed!"); + else { + Socket_destroy(newSocket); + DEBUG_PRINT("Connection attempt failed!\n"); + } } else { @@ -2664,19 +3030,8 @@ serverThread (void* parameter) acceptConnection = false; } - if (acceptConnection && (self->connectionRequestHandler != NULL)) { - char ipAddress[60]; - - Socket_getPeerAddressStatic(newSocket, ipAddress); - - /* remove TCP port part */ - char* separator = strchr(ipAddress, ':'); - if (separator != NULL) - *separator = 0; - - acceptConnection = self->connectionRequestHandler(self->connectionRequestHandlerParameter, - ipAddress); - } + if (acceptConnection) + acceptConnection = callConnectionRequestHandler(self, newSocket); if (acceptConnection) { @@ -2697,8 +3052,36 @@ serverThread (void* parameter) } #endif - MasterConnection connection = - MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); + MasterConnection connection = NULL; + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) { + + char ipAddress[60]; + + char* ipAddrStr = getPeerAddress(newSocket, ipAddress); + + CS104_RedundancyGroup matchingGroup = getMatchingRedundancyGroup(self, ipAddrStr); + + if (matchingGroup != NULL) { + connection = MasterConnection_createEx(self, newSocket, matchingGroup); + + if (matchingGroup->name) { + DEBUG_PRINT("Add connection to group: %s\n", matchingGroup->name); + } + } + else { + DEBUG_PRINT("Found no matching redundancy group -> close connection\n"); + } + + + } + else { + connection = MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); + } +#else + connection = MasterConnection_create(self, newSocket, lowPrioQueue, highPrioQueue); +#endif if (connection) { @@ -2737,6 +3120,28 @@ CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu) MessageQueue_enqueueASDU(self->asduQueue, asdu); #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) { + + /************************************************ + * Dispatch event to all redundancy groups + ************************************************/ + + LinkedList element = LinkedList_getNext(self->redundancyGroups); + + while (element) { + + CS104_RedundancyGroup group = (CS104_RedundancyGroup) LinkedList_getData(element); + + MessageQueue_enqueueASDU(group->asduQueue, asdu); + + element = LinkedList_getNext(element); + } + } + +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ + #if (CONFIG_CS104_SUPPORT_SERVER_MODE_CONNECTION_IS_REDUNDANCY_GROUP == 1) if (self->serverMode == CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP) { @@ -2766,6 +3171,44 @@ CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu) #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ } +void +CS104_Slave_addRedundancyGroup(CS104_Slave self, CS104_RedundancyGroup redundancyGroup) +{ +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) { + + if (self->redundancyGroups == NULL) + self->redundancyGroups = LinkedList_create(); + + LinkedList_add(self->redundancyGroups, redundancyGroup); + } + +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ +} + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) +static void +initializeRedundancyGroups(CS104_Slave self, int lowPrioMaxQueueSize, int highPrioMaxQueueSize) +{ + if (self->redundancyGroups == NULL) { + CS104_RedundancyGroup redGroup = CS104_RedundancyGroup_create(NULL); + CS104_Slave_addRedundancyGroup(self, redGroup); + } + + LinkedList element = LinkedList_getNext(self->redundancyGroups); + + while (element) { + + CS104_RedundancyGroup redGroup = (CS104_RedundancyGroup) LinkedList_getData(element); + + if (redGroup->asduQueue == NULL) + CS104_RedundancyGroup_initializeMessageQueues(redGroup, lowPrioMaxQueueSize, highPrioMaxQueueSize); + + element = LinkedList_getNext(element); + } +} +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ + void CS104_Slave_start(CS104_Slave self) { @@ -2778,6 +3221,12 @@ CS104_Slave_start(CS104_Slave self) if (self->serverMode == CS104_MODE_SINGLE_REDUNDANCY_GROUP) initializeMessageQueues(self, self->maxLowPrioQueueSize, self->maxHighPrioQueueSize); #endif + +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) + initializeRedundancyGroups(self, self->maxLowPrioQueueSize, self->maxHighPrioQueueSize); +#endif + self->listeningThread = Thread_create(serverThread, (void*) self, false); Thread_start(self->listeningThread); @@ -2801,6 +3250,11 @@ CS104_Slave_startThreadless(CS104_Slave self) initializeMessageQueues(self, self->maxLowPrioQueueSize, self->maxHighPrioQueueSize); #endif +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) + initializeRedundancyGroups(self, self->maxLowPrioQueueSize, self->maxHighPrioQueueSize); +#endif + if (self->localAddress) self->serverSocket = TcpServerSocket_create(self->localAddress, self->tcpPort); else @@ -2942,6 +3396,16 @@ CS104_Slave_destroy(CS104_Slave self) } #endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_SINGLE_REDUNDANCY_GROUP == 1) */ +#if (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) + + if (self->serverMode == CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS) { + + if (self->redundancyGroups) + LinkedList_destroyDeep(self->redundancyGroups, (LinkedListValueDeleteFunction) CS104_RedundancyGroup_destroy); + } + +#endif /* (CONFIG_CS104_SUPPORT_SERVER_MODE_MULTIPLE_REDUNDANCY_GROUPS == 1) */ + #if (CONFIG_CS104_SLAVE_POOL == 1) ReleaseSlave(self); #else diff --git a/lib60870-C/src/inc/api/cs104_slave.h b/lib60870-C/src/inc/api/cs104_slave.h index 2a0be45f..40d28f16 100644 --- a/lib60870-C/src/inc/api/cs104_slave.h +++ b/lib60870-C/src/inc/api/cs104_slave.h @@ -51,9 +51,18 @@ typedef struct sCS104_Slave* CS104_Slave; typedef enum { CS104_MODE_SINGLE_REDUNDANCY_GROUP, - CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP + CS104_MODE_CONNECTION_IS_REDUNDANCY_GROUP, + CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS } CS104_ServerMode; +typedef enum +{ + IP_ADDRESS_TYPE_IPV4, + IP_ADDRESS_TYPE_IPV6 +} eCS104_IPAddressType; + +typedef struct sCS104_RedundancyGroup* CS104_RedundancyGroup; + /** * \brief Connection request handler is called when a client tries to connect to the server. * @@ -221,9 +230,15 @@ CS104_Slave_setClockSyncHandler(CS104_Slave self, CS101_ClockSynchronizationHand void CS104_Slave_setRawMessageHandler(CS104_Slave self, CS104_SlaveRawMessageHandler handler, void* parameter); +/** + * \brief Get the APCI parameters instance. APCI parameters are CS 104 specific parameters. + */ CS104_APCIParameters CS104_Slave_getConnectionParameters(CS104_Slave self); +/** + * \brief Get the application layer parameters instance.. + */ CS101_AppLayerParameters CS104_Slave_getAppLayerParameters(CS104_Slave self); @@ -238,16 +253,42 @@ CS104_Slave_start(CS104_Slave self); bool CS104_Slave_isRunning(CS104_Slave self); +/** + * \brief Stop the server. + * + * Stop listening to incoming TCP/IP connections and close all open connections. + * Event buffers will be deactivated. + */ void CS104_Slave_stop(CS104_Slave self); - +/** + * \brief Start the slave (server) in non-threaded mode. + * + * Start listening to incoming TCP/IP connections. + * + * NOTE: Server should only be started after all configuration is done. + */ void CS104_Slave_startThreadless(CS104_Slave self); +/** + * \brief Stop the server in non-threaded mode + * + * Stop listening to incoming TCP/IP connections and close all open connections. + * Event buffers will be deactivated. + */ void CS104_Slave_stopThreadless(CS104_Slave self); +/** + * \brief Protocol stack tick function for non-threaded mode. + * + * Handle incoming connection requests and messages, send buffered events, and + * handle periodic tasks. + * + * NOTE: This function has to be called periodically by the application. + */ void CS104_Slave_tick(CS104_Slave self); @@ -259,9 +300,52 @@ CS104_Slave_tick(CS104_Slave self); void CS104_Slave_enqueueASDU(CS104_Slave self, CS101_ASDU asdu); +/** + * \brief Add a new redundancy group to the server. + * + * A redundancy group is a group of clients that share the same event queue. This function can + * only be used with server mode CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS. + * + * NOTE: Has to be called before the server is started! + * + * \param redundancyGroup the new redundancy group + */ +void +CS104_Slave_addRedundancyGroup(CS104_Slave self, CS104_RedundancyGroup redundancyGroup); + +/** + * \brief Delete the slave instance. Release all resources. + */ void CS104_Slave_destroy(CS104_Slave self); +/** + * \brief Create a new redundancy group. + * + * A redundancy group is a group of clients that share the same event queue. Redundancy groups can + * only be used with server mode CS104_MODE_MULTIPLE_REDUNDANCY_GROUPS. + */ +CS104_RedundancyGroup +CS104_RedundancyGroup_create(const char* name); + +/** + * \brief Add an allowed client to the redundancy group + * + * \param ipAddress the IP address of the client as C string (can be IPv4 or IPv6 address). + */ +void +CS104_RedundancyGroup_addAllowedClient(CS104_RedundancyGroup self, const char* ipAddress); + +/** + * \brief Add an allowed client to the redundancy group + * + * \param ipAddress the IP address as byte buffer (4 byte for IPv4, 16 byte for IPv6) + * \param addressType type of the IP address (either IP_ADDRESS_TYPE_IPV4 or IP_ADDRESS_TYPE_IPV6) + */ +void +CS104_RedundancyGroup_addAllowedClientEx(CS104_RedundancyGroup self, uint8_t* ipAddress, eCS104_IPAddressType addressType); + + /** * @} */ diff --git a/lib60870-C/tests/all_tests.c b/lib60870-C/tests/all_tests.c index 70e60672..2e235325 100644 --- a/lib60870-C/tests/all_tests.c +++ b/lib60870-C/tests/all_tests.c @@ -2,6 +2,8 @@ #include "iec60870_common.h" #include "hal_time.h" #include "buffer_frame.h" +#include +#include void setUp(void) { } void tearDown(void) {} @@ -16,6 +18,84 @@ static struct sCS101_AppLayerParameters defaultAppLayerParameters = { /* .maxSizeOfASDU = */ 249 }; +typedef enum +{ + IP_ADDRESS_TYPE_IPV4, + IP_ADDRESS_TYPE_IPV6 +} eCS104_IPAddressType; + +typedef struct sCS104_IPAddress* CS104_IPAddress; + +struct sCS104_IPAddress +{ + uint8_t address[16]; + eCS104_IPAddressType type; +}; + +static void +CS104_IPAddress_setFromString(CS104_IPAddress self, const char* ipAddrStr) +{ + if (strchr(ipAddrStr, '.') != NULL) { + /* parse IPv4 string */ + self->type = IP_ADDRESS_TYPE_IPV4; + + int i; + + for (i = 0; i < 4; i++) { + self->address[i] = strtoul(ipAddrStr, NULL, 10); + + ipAddrStr = strchr(ipAddrStr, '.'); + + if ((ipAddrStr == NULL) || (*ipAddrStr == 0)) + break; + + ipAddrStr++; + } + } + else { + self->type = IP_ADDRESS_TYPE_IPV6; + + int i; + + for (i = 0; i < 8; i++) { + uint32_t val = strtoul(ipAddrStr, NULL, 16); + + self->address[i * 2] = val / 0x100; + self->address[i * 2 + 1] = val % 0x100; + + ipAddrStr = strchr(ipAddrStr, ':'); + + if ((ipAddrStr == NULL) || (*ipAddrStr == 0)) + break; + + ipAddrStr++; + } + } +} + +static bool +CS104_IPAddress_equals(CS104_IPAddress self, CS104_IPAddress other) +{ + if (self->type != other->type) + return false; + + int size; + + if (self->type == IP_ADDRESS_TYPE_IPV4) + size = 4; + else + size = 16; + + int i; + + for (i = 0; i < size; i++) { + if (self->address[i] != other->address[i]) + return false; + } + + return true; +} + void CS101_ASDU_encode(CS101_ASDU self, Frame frame); @@ -180,6 +260,40 @@ test_EventOfProtectionEquipmentWithTime(void) TEST_ASSERT_EQUAL_INT(0, qdp); } + +void +test_IpAddressHandling(void) +{ + struct sCS104_IPAddress ipAddr1; + + CS104_IPAddress_setFromString(&ipAddr1, "192.168.34.25"); + + TEST_ASSERT_EQUAL_INT(IP_ADDRESS_TYPE_IPV4, ipAddr1.type); + TEST_ASSERT_EQUAL_UINT8(192, ipAddr1.address[0]); + TEST_ASSERT_EQUAL_UINT8(168, ipAddr1.address[1]); + TEST_ASSERT_EQUAL_UINT8(34, ipAddr1.address[2]); + TEST_ASSERT_EQUAL_UINT8(25, ipAddr1.address[3]); + + CS104_IPAddress_setFromString(&ipAddr1, "1:22:333:aaaa:b:c:d:e"); + TEST_ASSERT_EQUAL_INT(IP_ADDRESS_TYPE_IPV6, ipAddr1.type); + TEST_ASSERT_EQUAL_UINT8(0x00, ipAddr1.address[0]); + TEST_ASSERT_EQUAL_UINT8(0x01, ipAddr1.address[1]); + TEST_ASSERT_EQUAL_UINT8(0x00, ipAddr1.address[2]); + TEST_ASSERT_EQUAL_UINT8(0x22, ipAddr1.address[3]); + TEST_ASSERT_EQUAL_UINT8(0x03, ipAddr1.address[4]); + TEST_ASSERT_EQUAL_UINT8(0x33, ipAddr1.address[5]); + TEST_ASSERT_EQUAL_UINT8(0xaa, ipAddr1.address[6]); + TEST_ASSERT_EQUAL_UINT8(0xaa, ipAddr1.address[7]); + TEST_ASSERT_EQUAL_UINT8(0x00, ipAddr1.address[8]); + TEST_ASSERT_EQUAL_UINT8(0x0b, ipAddr1.address[9]); + TEST_ASSERT_EQUAL_UINT8(0x00, ipAddr1.address[10]); + TEST_ASSERT_EQUAL_UINT8(0x0c, ipAddr1.address[11]); + TEST_ASSERT_EQUAL_UINT8(0x00, ipAddr1.address[12]); + TEST_ASSERT_EQUAL_UINT8(0x0d, ipAddr1.address[13]); + TEST_ASSERT_EQUAL_UINT8(0x00, ipAddr1.address[14]); + TEST_ASSERT_EQUAL_UINT8(0x0e, ipAddr1.address[15]); +} + int main(int argc, char** argv) { @@ -190,5 +304,6 @@ main(int argc, char** argv) RUN_TEST(test_addMaxNumberOfIOsToASDU); RUN_TEST(test_SingleEventType); RUN_TEST(test_EventOfProtectionEquipmentWithTime); + RUN_TEST(test_IpAddressHandling); return UNITY_END(); } From 27695734bb98f275c9423e87dd969966d4d027d4 Mon Sep 17 00:00:00 2001 From: Michael Zillgith Date: Wed, 17 Oct 2018 10:22:25 +0200 Subject: [PATCH 24/24] - added default case in ASDU handling --- lib60870-C/src/iec60870/cs101/cs101_asdu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib60870-C/src/iec60870/cs101/cs101_asdu.c b/lib60870-C/src/iec60870/cs101/cs101_asdu.c index f86412a5..e9457b27 100644 --- a/lib60870-C/src/iec60870/cs101/cs101_asdu.c +++ b/lib60870-C/src/iec60870/cs101/cs101_asdu.c @@ -1139,6 +1139,10 @@ CS101_ASDU_getElementEx(CS101_ASDU self, InformationObject io, int index) self->payload, self->payloadSize, index * (self->parameters->sizeOfIOA + elementSize), false); break; + + default: + DEBUG_PRINT("type %d not supported\n", CS101_ASDU_getTypeID(self)); + break; } return retVal;