/* * sv_receiver.c * * Copyright 2015 Michael Zillgith * * This file is part of libIEC61850. * * libIEC61850 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * libIEC61850 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with libIEC61850. If not, see . * * See COPYING file for the complete license text. */ #include "stack_config.h" #include "libiec61850_platform_includes.h" #include "hal_ethernet.h" #include "hal_thread.h" #include "ber_decode.h" #include "ber_encoder.h" #include "sv_subscriber.h" #ifndef DEBUG_SV_SUBSCRIBER #define DEBUG_SV_SUBSCRIBER 1 #endif #define ETH_BUFFER_LENGTH 1518 #define ETH_P_SV 0x88ba struct sSVReceiver { bool running; bool stopped; bool checkDestAddr; /* option: check destination address (additionally to AppID) to identify application */ char* interfaceId; uint8_t* buffer; EthernetSocket ethSocket; LinkedList subscriberList; #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore subscriberListLock; #endif }; struct sSVSubscriber { uint8_t ethAddr[6]; uint16_t appId; SVUpdateListener listener; void* listenerParameter; }; struct sSVSubscriber_ASDU { char* svId; uint8_t* smpCnt; uint8_t* confRev; uint8_t* refrTm; uint8_t* smpSynch; int dataBufferLength; uint8_t* dataBuffer; }; SVReceiver SVReceiver_create(void) { SVReceiver self = (SVReceiver) GLOBAL_CALLOC(1, sizeof(struct sSVReceiver)); if (self != NULL) { self->subscriberList = LinkedList_create(); self->buffer = (uint8_t*) GLOBAL_MALLOC(ETH_BUFFER_LENGTH); self->checkDestAddr = false; #if (CONFIG_MMS_THREADLESS_STACK == 0) self->subscriberListLock = Semaphore_create(1); #endif } return self; } void SVReceiver_setInterfaceId(SVReceiver self, const char* interfaceId) { if (self->interfaceId != NULL) GLOBAL_FREEMEM(self->interfaceId); self->interfaceId = StringUtils_copyString(interfaceId); } void SVReceiver_disableDestAddrCheck(SVReceiver self) { self->checkDestAddr = false; } void SVReceiver_addSubscriber(SVReceiver self, SVSubscriber subscriber) { #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_wait(self->subscriberListLock); #endif LinkedList_add(self->subscriberList, (void*) subscriber); #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_post(self->subscriberListLock); #endif } void SVReceiver_removeSubscriber(SVReceiver self, SVSubscriber subscriber) { #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_wait(self->subscriberListLock); #endif LinkedList_remove(self->subscriberList, (void*) subscriber); #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_post(self->subscriberListLock); #endif } static void svReceiverLoop(void* threadParameter) { SVReceiver self = (SVReceiver) threadParameter; self->running = true; self->stopped = false; SVReceiver_startThreadless(self); while (self->running) { if (SVReceiver_tick(self) == false) Thread_sleep(1); } SVReceiver_stopThreadless(self); self->stopped = true; } void SVReceiver_start(SVReceiver self) { Thread thread = Thread_create((ThreadExecutionFunction) svReceiverLoop, (void*) self, true); if (thread != NULL) { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: SV receiver started for interface %s\n", self->interfaceId); Thread_start(thread); } else { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Starting SV receiver failed for interface %s\n", self->interfaceId); } } void SVReceiver_stop(SVReceiver self) { self->running = false; while (self->stopped == false) Thread_sleep(1); } void SVReceiver_destroy(SVReceiver self) { LinkedList_destroyDeep(self->subscriberList, (LinkedListValueDeleteFunction) SVSubscriber_destroy); #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_destroy(self->subscriberListLock); #endif GLOBAL_FREEMEM(self->buffer); GLOBAL_FREEMEM(self); } void SVReceiver_startThreadless(SVReceiver self) { if (self->interfaceId == NULL) self->ethSocket = Ethernet_createSocket(CONFIG_ETHERNET_INTERFACE_ID, NULL); else self->ethSocket = Ethernet_createSocket(self->interfaceId, NULL); Ethernet_setProtocolFilter(self->ethSocket, ETH_P_SV); self->running = true; } void SVReceiver_stopThreadless(SVReceiver self) { Ethernet_destroySocket(self->ethSocket); self->running = false; } void SVReceiver_addHandleSet(SVReceiver self, EthernetHandleSet handles) { return EthernetHandleSet_addSocket(handles, self->ethSocket); } static void parseASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length) { int bufPos = 0; struct sSVSubscriber_ASDU asdu; memset(&asdu, 0, sizeof(struct sSVSubscriber_ASDU)); int svIdLength = 0; while (bufPos < length) { int elementLength; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, length); switch (tag) { case 0x80: asdu.svId = (char*) (buffer + bufPos); svIdLength = elementLength; break; case 0x82: asdu.smpCnt = buffer + bufPos; break; case 0x83: asdu.confRev = buffer + bufPos; break; case 0x84: asdu.refrTm = buffer + bufPos; break; case 0x85: asdu.smpSynch = buffer + bufPos; break; case 0x87: asdu.dataBuffer = buffer + bufPos; asdu.dataBufferLength = elementLength; break; default: /* ignore unknown tag */ break; } bufPos += elementLength; } if (asdu.svId != NULL) asdu.svId[svIdLength] = 0; /* Call callback handler */ if (subscriber->listener != NULL) subscriber->listener(subscriber, subscriber->listenerParameter, &asdu); } static void parseSequenceOfASDU(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int length) { int bufPos = 0; while (bufPos < length) { int elementLength; uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, length); switch (tag) { case 0x30: parseASDU(self, subscriber, buffer + bufPos, elementLength); break; default: /* ignore unknown tag */ break; } bufPos += elementLength; } } static void parseSVPayload(SVReceiver self, SVSubscriber subscriber, uint8_t* buffer, int apduLength) { int bufPos = 0; if (buffer[bufPos++] == 0x60) { int elementLength; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, apduLength); int svEnd = bufPos + elementLength; while (bufPos < svEnd) { uint8_t tag = buffer[bufPos++]; bufPos = BerDecoder_decodeLength(buffer, &elementLength, bufPos, svEnd); if (bufPos + elementLength > apduLength) { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Malformed message: sub element is too large!\n"); goto exit_error; } if (bufPos == -1) goto exit_error; switch(tag) { case 0x80: /* noASDU (INTEGER) */ /* ignore */ break; case 0xa2: /* asdu (SEQUENCE) */ parseSequenceOfASDU(self, subscriber, buffer + bufPos, elementLength); break; default: /* ignore unknown tag */ break; } bufPos += elementLength; } return; } exit_error: if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Invalid SV message!\n"); return; } static void parseSVMessage(SVReceiver self, int numbytes) { int bufPos; bool subscriberFound = false; uint8_t* buffer = self->buffer; if (numbytes < 22) return; /* Ethernet source address */ uint8_t* srcAddr = buffer; /* skip ethernet addresses */ bufPos = 12; int headerLength = 14; /* check for VLAN tag */ if ((buffer[bufPos] == 0x81) && (buffer[bufPos + 1] == 0x00)) { bufPos += 4; /* skip VLAN tag */ headerLength += 4; } /* check for SV Ethertype */ if (buffer[bufPos++] != 0x88) return; if (buffer[bufPos++] != 0xba) return; uint16_t appId; appId = buffer[bufPos++] * 0x100; appId += buffer[bufPos++]; uint16_t length; length = buffer[bufPos++] * 0x100; length += buffer[bufPos++]; /* skip reserved fields */ bufPos += 4; int apduLength = length - 8; if (numbytes < length + headerLength) { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Invalid PDU size\n"); return; } if (DEBUG_SV_SUBSCRIBER) { printf("SV_SUBSCRIBER: SV message: ----------------\n"); printf("SV_SUBSCRIBER: APPID: %u\n", appId); printf("SV_SUBSCRIBER: LENGTH: %u\n", length); printf("SV_SUBSCRIBER: APDU length: %i\n", apduLength); } /* check if there is a matching subscriber */ #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_wait(self->subscriberListLock); #endif LinkedList element = LinkedList_getNext(self->subscriberList); SVSubscriber subscriber; while (element != NULL) { subscriber = (SVSubscriber) LinkedList_getData(element); if (subscriber->appId == appId) { if (self->checkDestAddr) { if (memcmp(srcAddr, subscriber->ethAddr, 6) == 0) { subscriberFound = true; break; } else if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: Checking ethernet src address failed!\n"); } else { subscriberFound = true; break; } } element = LinkedList_getNext(element); } #if (CONFIG_MMS_THREADLESS_STACK == 0) Semaphore_post(self->subscriberListLock); #endif if (subscriberFound) parseSVPayload(self, subscriber, buffer + bufPos, apduLength); else { if (DEBUG_SV_SUBSCRIBER) printf("SV_SUBSCRIBER: SV message ignored due to unknown APPID value\n"); } } bool SVReceiver_tick(SVReceiver self) { int packetSize = Ethernet_receivePacket(self->ethSocket, self->buffer, ETH_BUFFER_LENGTH); if (packetSize > 0) { parseSVMessage(self, packetSize); return true; } else return false; } SVSubscriber SVSubscriber_create(const uint8_t* ethAddr, uint16_t appID) { SVSubscriber self = (SVSubscriber) GLOBAL_CALLOC(1, sizeof(struct sSVSubscriber)); if (self != NULL) { self->appId = appID; if (ethAddr != NULL) memcpy(self->ethAddr, ethAddr, 6); } return self; } void SVSubscriber_destroy(SVSubscriber self) { if (self != NULL) GLOBAL_FREEMEM(self); } void SVSubscriber_setListener(SVSubscriber self, SVUpdateListener listener, void* parameter) { self->listener = listener; self->listenerParameter = parameter; } uint16_t SVSubscriber_ASDU_getSmpCnt(SVSubscriber_ASDU self) { uint16_t retVal; uint8_t* valBytes = (uint8_t*) &retVal; #if (ORDER_LITTLE_ENDIAN == 1) valBytes[0] = self->smpCnt[1]; valBytes[1] = self->smpCnt[0]; #else valBytes[0] = self->smpCnt[0]; valBytes[1] = self->smpCnt[1]; #endif return retVal; } static uint64_t decodeUtcTime(uint8_t* buffer, uint8_t* timeQuality) { uint32_t timeval32; timeval32 = buffer[3]; timeval32 += buffer[2] * 0x100; timeval32 += buffer[1] * 0x10000; timeval32 += buffer[0] * 0x1000000; uint32_t msVal; uint32_t fractionOfSecond; fractionOfSecond = buffer[6]; fractionOfSecond += buffer[5] * 0x100; fractionOfSecond += buffer[4] * 0x10000; msVal = (uint32_t) (((uint64_t) fractionOfSecond * 1000) / 16777215); if (timeQuality != NULL) *timeQuality = buffer[7]; uint64_t timeval64 = (uint64_t) timeval32 * 1000 + (uint64_t) msVal; return timeval64; } uint64_t SVSubscriber_ASDU_getRefrTmAsMs(SVSubscriber_ASDU self) { uint64_t msTime = 0; if (self->refrTm != NULL) msTime = decodeUtcTime(self->refrTm, NULL); return msTime; } bool SVSubscriber_ASDU_hasRefrTm(SVSubscriber_ASDU self) { return (self->refrTm != NULL); } const char* SVSubscriber_ASDU_getSvId(SVSubscriber_ASDU self) { return self->svId; } uint32_t SVSubscriber_ASDU_getConfRev(SVSubscriber_ASDU self) { uint32_t retVal = *((uint32_t*) (self->confRev)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 4); #endif return retVal; } int8_t SVSubscriber_ASDU_getINT8(SVSubscriber_ASDU self, int index) { int8_t retVal = *((int8_t*) (self->dataBuffer + index)); return retVal; } int16_t SVSubscriber_ASDU_getINT16(SVSubscriber_ASDU self, int index) { int16_t retVal = *((int16_t*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 2); #endif return retVal; } int32_t SVSubscriber_ASDU_getINT32(SVSubscriber_ASDU self, int index) { int32_t retVal = *((int32_t*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 4); #endif return retVal; } int64_t SVSubscriber_ASDU_getINT64(SVSubscriber_ASDU self, int index) { int64_t retVal = *((int64_t*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 8); #endif return retVal; } uint8_t SVSubscriber_ASDU_getINT8U(SVSubscriber_ASDU self, int index) { uint8_t retVal = *((uint8_t*) (self->dataBuffer + index)); return retVal; } uint16_t SVSubscriber_ASDU_getINT16U(SVSubscriber_ASDU self, int index) { uint16_t retVal = *((uint16_t*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 2); #endif return retVal; } uint32_t SVSubscriber_ASDU_getINT32U(SVSubscriber_ASDU self, int index) { uint32_t retVal = *((uint32_t*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 4); #endif return retVal; } uint64_t SVSubscriber_ASDU_getINT64U(SVSubscriber_ASDU self, int index) { uint64_t retVal = *((uint64_t*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 8); #endif return retVal; } float SVSubscriber_ASDU_getFLOAT32(SVSubscriber_ASDU self, int index) { float retVal = *((float*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 4); #endif return retVal; } double SVSubscriber_ASDU_getFLOAT64(SVSubscriber_ASDU self, int index) { double retVal = *((double*) (self->dataBuffer + index)); #if (ORDER_LITTLE_ENDIAN == 1) uint8_t* buf = (uint8_t*) (&retVal); BerEncoder_revertByteOrder(buf, 8); #endif return retVal; } int SVSubscriber_ASDU_getDataSize(SVSubscriber_ASDU self) { return self->dataBufferLength; }