diff --git a/config/stack_config.h b/config/stack_config.h index 05e2c72..da4c5ad 100644 --- a/config/stack_config.h +++ b/config/stack_config.h @@ -182,10 +182,10 @@ #define CONFIG_IEC61850_CONTROL_SERVICE 0 #endif -/* support flatted named variable name space required by IEC 61850-8-1 MMS mapping */ +/* support flat named variable name space required by IEC 61850-8-1 MMS mapping */ #define CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE 1 -/* VMD scope named variables are not used by IEC 61850 */ +/* VMD scope named variables are not used by IEC 61850 (one application is ICCP) */ #define CONFIG_MMS_SUPPORT_VMD_SCOPE_NAMED_VARIABLES 0 #define CONFIG_INCLUDE_PLATFORM_SPECIFIC_HEADERS 0 diff --git a/src/common/inc/string_utilities.h b/src/common/inc/string_utilities.h index 9126701..7c46d67 100644 --- a/src/common/inc/string_utilities.h +++ b/src/common/inc/string_utilities.h @@ -25,6 +25,7 @@ #define STRING_UTILITIES_H_ #include "libiec61850_platform_includes.h" +#include "linked_list.h" char* copyString(const char* string); @@ -76,4 +77,34 @@ StringUtils_createBufferFromHexString(char* hexString, uint8_t* buffer); bool StringUtils_startsWith(char* string, char* prefix); +/** + * \brief Compare to characters using the collation order as defined in ISO 9506-2 7.5.2 + * + * \param a the first string + * \param b the second string + * + * \returns 0 if a equals b; a positive number if b > a; a negative number if b < a + */ +int +StringUtils_compareChars(char a, char b); + +/** + * \brief Compare to strings using the collation order as defined in ISO 9506-2 7.5.2 + * + * \param a the first string + * \param b the second string + * + * \returns 0 if a equals b; a positive number if b > a; a negative number if b < a + */ +int +StringUtils_compareStrings(const char* a, const char* b); + +/** + * \brief sort a list of strings alphabetically (according to the MMS identifier collation order) + * + * \param list a list that contains string elements + */ +void +StringUtils_sortList(LinkedList list); + #endif /* STRING_UTILITIES_H_ */ diff --git a/src/common/string_utilities.c b/src/common/string_utilities.c index d02c5f8..0004dec 100644 --- a/src/common/string_utilities.c +++ b/src/common/string_utilities.c @@ -240,3 +240,129 @@ StringUtils_startsWith(char* string, char* prefix) return false; } +#define LT_MAX_CHARS 128 + +static int +getCharWeight(int c) +{ + static bool initialized = false; + static char lookupTable[LT_MAX_CHARS + 1]; + static char* charOrder = "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz$_0123456789"; + + if (!initialized) { + int ltIndex; + int weight = 1; + + for (ltIndex = 1; ltIndex < LT_MAX_CHARS; ltIndex++) { + if (strchr(charOrder, ltIndex)) continue; + lookupTable[ltIndex] = weight; + weight++; + } + + int charIndex; + + for (charIndex = 0; charOrder[charIndex]; charIndex++) { + lookupTable[(int)charOrder[charIndex]] = weight; + weight++; + } + + initialized = true; + } + + if ((c < 1) || (c > LT_MAX_CHARS)) + return c; + else + return lookupTable[c]; +} + +int +StringUtils_compareChars(char a, char b) +{ + return (getCharWeight(a) - getCharWeight(b)); +} + +int +StringUtils_compareStrings(const char* a, const char* b) +{ + int diff = StringUtils_compareChars(*a, *b); + + while (diff == 0) { + if ((*a == 0) || (*b == 0)) { + return b - a; + } + + diff = StringUtils_compareChars(*++a, *++b); + } + + return diff; +} + +void +StringUtils_sortList(LinkedList list) +{ + LinkedList firstElement = list->next; + LinkedList prevElement = NULL; + + while (true) { + + /* Check for end of list */ + if (firstElement->next) { + + char* str1 = (char*) LinkedList_getData(firstElement); + char* str2 = (char*) LinkedList_getData(firstElement->next); + + /* Compare first element with next element */ + if (StringUtils_compareStrings(str1, str2) > 0) { + LinkedList removedElement = firstElement; + + if (firstElement == list->next) + list->next = firstElement->next; + else + prevElement->next = firstElement->next; + + firstElement = firstElement->next; + + /* search for place to insert */ + LinkedList compareElement = removedElement->next->next; + LinkedList prevCompareElement = removedElement->next; + + while (true) { + str2 = (char*) LinkedList_getData(compareElement); + + if (StringUtils_compareStrings(str1, str2) < 0) { + /* insert element before */ + prevCompareElement->next = removedElement; + removedElement->next = compareElement; + + break; + } + + prevCompareElement = compareElement; + compareElement = compareElement->next; + + /* Are we at the end of the list? */ + if (compareElement == NULL) { + /* Insert removed element at end of list */ + prevCompareElement->next = removedElement; + removedElement->next = NULL; + break; + } + + } + + } + else { + + prevElement = firstElement; + firstElement = firstElement->next; + } + + + } + else + break; + + } +} + + diff --git a/src/iec61850/server/mms_mapping/mms_goose.c b/src/iec61850/server/mms_mapping/mms_goose.c index 8b9d723..bb4963f 100644 --- a/src/iec61850/server/mms_mapping/mms_goose.c +++ b/src/iec61850/server/mms_mapping/mms_goose.c @@ -501,7 +501,6 @@ GOOSE_createGOOSEControlBlocks(MmsMapping* self, MmsDomain* domain, "$GO$", gooseControlBlock->name); if (gooseControlBlock->appId != NULL) { - // gcb->goID = copyString(gooseControlBlock->appId); MmsValue* goID = MmsValue_getElement(gseValues, 1); MmsValue_setVisibleString(goID, gooseControlBlock->appId); diff --git a/src/iec61850/server/mms_mapping/reporting.c b/src/iec61850/server/mms_mapping/reporting.c index b3513b0..8d06212 100644 --- a/src/iec61850/server/mms_mapping/reporting.c +++ b/src/iec61850/server/mms_mapping/reporting.c @@ -432,7 +432,7 @@ sendReport(ReportControl* self, bool isIntegrity, bool isGI) if (self->sqNum == 256) self->sqNum = 0; - MmsValue_setUint8(sqNum, self->sqNum); + MmsValue_setUint16(sqNum, self->sqNum); LinkedList_destroyDeep(deletableElements, (LinkedListValueDeleteFunction) MmsValue_delete); LinkedList_destroyStatic(reportElements); @@ -1225,7 +1225,7 @@ Reporting_RCBWriteAccessHandler(MmsMapping* self, ReportControl* rc, char* eleme MmsValue* sqNum = ReportControl_getRCBValue(rc, "SqNum"); - MmsValue_setUint16(sqNum, 0U); + MmsValue_setUint32(sqNum, 0U); retVal = DATA_ACCESS_ERROR_SUCCESS; goto exit_function; @@ -2165,7 +2165,7 @@ sendNextReportEntry(ReportControl* self) /* Increase sequence number */ self->sqNum++; - MmsValue_setUint16(sqNum, self->sqNum); + MmsValue_setUint32(sqNum, self->sqNum); assert(self->reportBuffer->nextToTransmit != self->reportBuffer->nextToTransmit->next); diff --git a/src/mms/iso_mms/server/mms_get_namelist_service.c b/src/mms/iso_mms/server/mms_get_namelist_service.c index 0bb0af1..4bc4048 100644 --- a/src/mms/iso_mms/server/mms_get_namelist_service.c +++ b/src/mms/iso_mms/server/mms_get_namelist_service.c @@ -1,7 +1,7 @@ /* * mms_get_namelist_service.c * - * Copyright 2013, 2014 Michael Zillgith + * Copyright 2013, 2014, 2015 Michael Zillgith * * This file is part of libIEC61850. * @@ -125,10 +125,12 @@ addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpec return listElement; } + #endif /* (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) */ static LinkedList -getNameListDomainSpecific(MmsServerConnection connection, char* domainName) { +getNameListDomainSpecific(MmsServerConnection connection, char* domainName) +{ MmsDevice* device = MmsServer_getDevice(connection->server); LinkedList nameList = NULL; @@ -157,6 +159,85 @@ getNameListDomainSpecific(MmsServerConnection connection, char* domainName) { return nameList; } + + +#if 0 +typedef struct sGetNextListElementDomainSpecificState { + const char* continueAfter; + MmsVariableSpecification** variables; + int variablesCount; +} GetNextListElementDomainSpecificState; + + +static bool +getNextListElement_DomainSpecific_init(MmsServerConnection connection, char* domainName, + GetNextListElementDomainSpecificState* state, const char* continueAfter) +{ + MmsDevice* device = MmsServer_getDevice(connection->server); + MmsDomain* domain = MmsDevice_getDomain(device, domainName); + + state->continueAfter = continueAfter; + + if (domain != NULL) { + state->variables = domain->namedVariables; + state->variablesCount = domain->namedVariablesCount; + } +} + + +static LinkedList +TEMP_addSubNamedVaribleNamesToList(LinkedList nameList, char* prefix, MmsVariableSpecification* variable) +{ + LinkedList listElement = nameList; + + if (variable->type == MMS_STRUCTURE) { + + int i; + + MmsVariableSpecification** variables = variable->typeSpec.structure.elements; + + for (i = 0; i < variable->typeSpec.structure.elementCount; i++) { + char* variableName = appendMmsSubVariable(prefix, variables[i]->name); + + listElement = LinkedList_insertAfter(listElement, variableName); + + listElement = addSubNamedVaribleNamesToList(listElement, variableName, variables[i]); + } + } + + return listElement; +} + + +// return NULL if no more elements are left +static char* +getNextListElement_DomainSpecific(void* stateParameter) +{ + GetNextListElementDomainSpecificState* state = (GetNextListElementDomainSpecificState*) stateParameter; + + = NULL; + + int i; + + LinkedList element = nameList; + + for (i = 0; i < state->variablesCount; i++) { + element = LinkedList_insertAfter(element, copyString(state->variables[i]->name)); + +#if (CONFIG_MMS_SUPPORT_FLATTED_NAME_SPACE == 1) + char* prefix = state->variables[i]->name; + element = TEMP_addSubNamedVaribleNamesToList(element, prefix, state->variables[i]); +#endif + + } + + + return nameList; +} +#endif + + + #if (MMS_DATA_SET_SERVICE == 1) static LinkedList @@ -325,9 +406,125 @@ createNameListResponse( response->size = bufPos; if (DEBUG_MMS_SERVER) - printf("getNameList: encoded %i bytes\n", response->size); + printf("MMS_SERVER: getNameList: encoded %i bytes\n", response->size); } +#if 0 +static void +NEW_createNameListResponse( + MmsServerConnection connection, + int invokeId, + LinkedList nameList, + ByteBuffer* response, + char* continueAfter) +{ + + +#if 0 + + //TODO move continueAfter handling in variable name generation code! + LinkedList startElement = NULL; + + + if (continueAfter != NULL) { + LinkedList element = nameList; + + while ((element = LinkedList_getNext(element)) != NULL) { + if (strcmp((char*) (element->data), continueAfter) == 0) { + startElement = element; + break; + } + } + + if (startElement == NULL) { + mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); + return; + } + } + + /* determine number of identifiers to include in response */ + if (startElement == NULL) + startElement = nameList; +#endif + + + + int nameCount = 0; + int estimatedMmsPduLength = 27; /* estimated overhead size of PDU encoding */ + int maxPduSize = connection->maxPduSize; + + bool moreFollows = false; + + LinkedList element = startElement; + + uint32_t identifierListSize = 0; + + while ((element = LinkedList_getNext(element)) != NULL) { + int elementLength; + + elementLength = BerEncoder_determineEncodedStringSize((char*) element->data); + + if ((estimatedMmsPduLength + elementLength) > maxPduSize) { + moreFollows = true; + break; + } + else { + estimatedMmsPduLength += elementLength; + identifierListSize += elementLength; + nameCount++; + } + + } + + uint32_t listOfIdentifierSize = 1 + BerEncoder_determineLengthSize(identifierListSize) + identifierListSize; + + uint32_t getNameListSize = listOfIdentifierSize; + + if (moreFollows == false) + getNameListSize += 3; + + uint32_t confirmedServiceResponseSize = 1 + BerEncoder_determineLengthSize(getNameListSize) + getNameListSize; + + uint32_t invokeIdSize = BerEncoder_UInt32determineEncodedSize((uint32_t) invokeId) + 2; + + uint32_t confirmedResponsePDUSize = confirmedServiceResponseSize + invokeIdSize; + + /* encode response */ + element = startElement; + + uint8_t* buffer = response->buffer; + int bufPos = 0; + + bufPos = BerEncoder_encodeTL(0xa1, confirmedResponsePDUSize, buffer, bufPos); + + bufPos = BerEncoder_encodeTL(0x02, invokeIdSize - 2, buffer, bufPos); + bufPos = BerEncoder_encodeUInt32((uint32_t) invokeId, buffer, bufPos); + + bufPos = BerEncoder_encodeTL(0xa1, getNameListSize, buffer, bufPos); + bufPos = BerEncoder_encodeTL(0xa0, identifierListSize, buffer, bufPos); + + int i = 0; + + while ((element = LinkedList_getNext(element)) != NULL) { + bufPos = BerEncoder_encodeStringWithTag(0x1a, (char*) element->data, buffer, bufPos); + + i++; + + if (i == nameCount) + break; + } + + if (moreFollows == false) + bufPos = BerEncoder_encodeBoolean(0x81, moreFollows, buffer, bufPos); + + response->size = bufPos; + + if (DEBUG_MMS_SERVER) + printf("MMS_SERVER: getNameList: encoded %i bytes\n", response->size); +} +#endif + + void mmsServer_handleGetNameListRequest( MmsServerConnection connection, @@ -352,7 +549,7 @@ mmsServer_handleGetNameListRequest( bufPos = BerDecoder_decodeLength(buffer, &length, bufPos, maxBufPos); if (bufPos < 0) { - //writeMmsRejectPdu(&invokeId, REJECT_UNRECOGNIZED_SERVICE, response); + mmsServer_writeMmsRejectPdu(&invokeId, MMS_ERROR_REJECT_INVALID_PDU, response); return; } @@ -410,7 +607,7 @@ mmsServer_handleGetNameListRequest( continueAfterId[continueAfterLength] = 0; if (DEBUG_MMS_SERVER) - printf("continue after: (%s)\n", continueAfterId); + printf("MMS_SERVER: getNameListRequest - continue after: (%s)\n", continueAfterId); } if (objectScope == OBJECT_SCOPE_DOMAIN) { @@ -421,13 +618,23 @@ mmsServer_handleGetNameListRequest( if (objectClass == OBJECT_CLASS_NAMED_VARIABLE) { if (DEBUG_MMS_SERVER) - printf("get namelist for (%s)\n", domainSpecificName); + printf("MMS_SERVER: get namelist for (%s)\n", domainSpecificName); LinkedList nameList = getNameListDomainSpecific(connection, domainSpecificName); if (nameList == NULL) mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); else { + + printf("\n-------------\n"); + LinkedList_printStringList(nameList); + + StringUtils_sortList(nameList); + + printf("\n-------------\n"); + LinkedList_printStringList(nameList); + printf("\n-------------\n"); + createNameListResponse(connection, invokeId, nameList, response, continueAfterId); LinkedList_destroy(nameList); } @@ -439,6 +646,8 @@ mmsServer_handleGetNameListRequest( if (nameList == NULL) mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_NON_EXISTENT); else { + StringUtils_sortList(nameList); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroy(nameList); } @@ -458,6 +667,8 @@ mmsServer_handleGetNameListRequest( LinkedList nameList = getDomainNames(connection); + StringUtils_sortList(nameList); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroyStatic(nameList); @@ -477,6 +688,8 @@ mmsServer_handleGetNameListRequest( else if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { LinkedList nameList = getnamedVariableListsVMDSpecific(connection); + StringUtils_sortList(nameList); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroy(nameList); @@ -486,6 +699,8 @@ mmsServer_handleGetNameListRequest( else if (objectClass == OBJECT_CLASS_JOURNAL) { LinkedList nameList = LinkedList_create(); + // StringUtils_sortList(nameList); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroy(nameList); @@ -505,6 +720,8 @@ mmsServer_handleGetNameListRequest( if (objectClass == OBJECT_CLASS_NAMED_VARIABLE_LIST) { LinkedList nameList = getNamedVariableListAssociationSpecific(connection); + StringUtils_sortList(nameList); + createNameListResponse(connection, invokeId, nameList, response, continueAfter); LinkedList_destroy(nameList); @@ -516,7 +733,7 @@ mmsServer_handleGetNameListRequest( #endif /* (MMS_DATA_SET_SERVICE == 1) */ else { - if (DEBUG_MMS_SERVER) printf("mms_server: getNameList(%i) not supported!\n", objectScope); + if (DEBUG_MMS_SERVER) printf("MMS_SERVER: getNameList(%i) not supported!\n", objectScope); mmsServer_createConfirmedErrorPdu(invokeId, response, MMS_ERROR_ACCESS_OBJECT_ACCESS_UNSUPPORTED); } diff --git a/src/mms/iso_mms/server/mms_server_connection.c b/src/mms/iso_mms/server/mms_server_connection.c index 0e7e68f..d611993 100644 --- a/src/mms/iso_mms/server/mms_server_connection.c +++ b/src/mms/iso_mms/server/mms_server_connection.c @@ -62,6 +62,11 @@ mmsServer_writeMmsRejectPdu(uint32_t* invokeId, int reason, ByteBuffer* response mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU = RejectPDU__rejectReason__confirmedRequestPDU_invalidArgument; } + else if (reason == MMS_ERROR_REJECT_INVALID_PDU) { + mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_pduError; + mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU = + RejectPDU__rejectReason__pduError_invalidPdu; + } else { mmsPdu->choice.rejectPDU.rejectReason.present = RejectPDU__rejectReason_PR_confirmedRequestPDU; mmsPdu->choice.rejectPDU.rejectReason.choice.confirmedResponsePDU =