The goal of this software is to automatically generate C/C++ code which reads and writes GOOSE and Sampled Value packets. Any valid IEC 61850 Substation Configuration Description (SCD) file, describing GOOSE and/or SV communications, can be used as the input. The output code is lightweight and platform-independent, so it can run on a variety of devices, including low-cost microcontrollers. It\'s ideal for rapid-prototyping new protection and control systems that require communications. This mbed project is a simple example of this functionality. Other code: https://github.com/stevenblair/rapid61850 Project homepage: http://personal.strath.ac.uk/steven.m.blair/
Diff: gseEncodePacket.c
- Revision:
- 1:9399d44c2b1a
- Parent:
- 0:230c10b228ea
--- a/gseEncodePacket.c Fri Oct 07 13:41:08 2011 +0000 +++ b/gseEncodePacket.c Fri Oct 07 13:48:18 2011 +0000 @@ -1,148 +1,168 @@ +/** + * Rapid-prototyping protection schemes with IEC 61850 + * + * Copyright (c) 2011 Steven Blair + * + * This program 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 2 + * of the License, or (at your option) any later version. + + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + #include "encodePacket.h" #include "gsePacketData.h" #include "gseEncode.h" #include "svEncode.h" int getGseHeaderLength(struct gseData *gseData) { - int size = 0; - int len = 0; + int size = 0; + int len = 0; - size = strlen((const char *) gseData->gocbRef); - len += size + getLengthBytes(size) + 1; + size = strlen((const char *) gseData->gocbRef); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->timeAllowedToLive); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->timeAllowedToLive); + len += size + getLengthBytes(size) + 1; - size = strlen((const char *) gseData->datSet); - len += size + getLengthBytes(size) + 1; + size = strlen((const char *) gseData->datSet); + len += size + getLengthBytes(size) + 1; - size = strlen((const char *) gseData->goID); - len += size + getLengthBytes(size) + 1; + size = strlen((const char *) gseData->goID); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->stNum); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->stNum); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->sqNum); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->sqNum); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->test); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->test); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->confRev); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->confRev); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->ndsCom); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->ndsCom); + len += size + getLengthBytes(size) + 1; - size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->numDatSetEntries); - len += size + getLengthBytes(size) + 1; + size = BER_GET_LENGTH_CTYPE_INT32U(&gseData->numDatSetEntries); + len += size + getLengthBytes(size) + 1; - size = (gseData->getDatasetLength)(); - len += size + getLengthBytes(size) + 1; + size = (gseData->getDatasetLength)(); + len += size + getLengthBytes(size) + 1; - return len; + return len; } // creates a GSE packet, including frame header. returns 0 on fail; number of bytes on success int gseEncodePacket(struct gseData *gseData, unsigned char *buf) { - int offset = 0; - int size = 0; - int ADPULength = getGseHeaderLength(gseData); - int len = ADPULength + 9 + getLengthBytes(ADPULength); // APDU tag size (1 byte), plus 8 "header" bytes + int offset = 0; + int size = 0; + int ADPULength = getGseHeaderLength(gseData); + int len = ADPULength + 9 + getLengthBytes(ADPULength); // APDU tag size (1 byte), plus 8 "header" bytes - //printf("ADPULength: %i, len: %i\n", ADPULength, len); + //printf("ADPULength: %i, len: %i\n", ADPULength, len); - // frame header - memcpy(&buf[offset], gseData->ethHeaderData.destMACAddress, 6); // destination MAC addresses - offset += 6; - memcpy(&buf[offset], LOCAL_MAC_ADDRESS, 6); // source MAC addresses - offset += 6; + // frame header + memcpy(&buf[offset], gseData->ethHeaderData.destMACAddress, 6); // destination MAC addresses + offset += 6; + memcpy(&buf[offset], LOCAL_MAC_ADDRESS, 6); // source MAC addresses + offset += 6; - buf[offset++] = 0x81; // TPID - buf[offset++] = 0x00; + buf[offset++] = 0x81; // TPID + buf[offset++] = 0x00; - netmemcpy(&buf[offset], &gseData->ethHeaderData.VLAN_ID, 2); // TCI - buf[offset] |= (gseData->ethHeaderData.VLAN_PRIORITY << 5); - offset += 2; + netmemcpy(&buf[offset], &gseData->ethHeaderData.VLAN_ID, 2); // TCI + buf[offset] |= (gseData->ethHeaderData.VLAN_PRIORITY << 5); + offset += 2; - buf[offset++] = 0x88; // EtherType - buf[offset++] = 0xB8; + buf[offset++] = 0x88; // EtherType + buf[offset++] = 0xB8; - netmemcpy(&buf[offset], &gseData->ethHeaderData.APPID, 2); // APPID - offset += 2; + netmemcpy(&buf[offset], &gseData->ethHeaderData.APPID, 2); // APPID + offset += 2; - netmemcpy(&buf[offset], &len, 2); // length - offset += 2; + netmemcpy(&buf[offset], &len, 2); // length + offset += 2; - buf[offset++] = 0x00; // reserved 1 - buf[offset++] = 0x00; - buf[offset++] = 0x00; // reserved 2 - buf[offset++] = 0x00; + buf[offset++] = 0x00; // reserved 1 + buf[offset++] = 0x00; + buf[offset++] = 0x00; // reserved 2 + buf[offset++] = 0x00; - buf[offset++] = 0x61; - offset += encodeLength(&buf[offset], ADPULength /*+ getLengthBytes(ADPULength) + 1*/); + buf[offset++] = 0x61; + offset += encodeLength(&buf[offset], ADPULength /*+ getLengthBytes(ADPULength) + 1*/); - buf[offset++] = 0x80; - size = strlen((const char *) gseData->gocbRef); - buf[offset++] = size; - memcpy(&buf[offset], gseData->gocbRef, size); - offset += size; + buf[offset++] = 0x80; + size = strlen((const char *) gseData->gocbRef); + buf[offset++] = size; + memcpy(&buf[offset], gseData->gocbRef, size); + offset += size; - buf[offset++] = 0x81; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->timeAllowedToLive)); - //offset += BER_ENCODE_CTYPE_INT32U(&buf[offset], &gseData->timeAllowedToLive); - offset += ber_encode_integer(&buf[offset], &gseData->timeAllowedToLive, SV_GET_LENGTH_INT32U); + buf[offset++] = 0x81; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->timeAllowedToLive)); + //offset += BER_ENCODE_CTYPE_INT32U(&buf[offset], &gseData->timeAllowedToLive); + offset += ber_encode_integer(&buf[offset], &gseData->timeAllowedToLive, SV_GET_LENGTH_INT32U); - buf[offset++] = 0x82; - size = strlen((const char *) gseData->datSet); - buf[offset++] = size; - memcpy(&buf[offset], gseData->datSet, size); - offset += size; + buf[offset++] = 0x82; + size = strlen((const char *) gseData->datSet); + buf[offset++] = size; + memcpy(&buf[offset], gseData->datSet, size); + offset += size; - buf[offset++] = 0x83; - size = strlen((const char *) gseData->goID); - buf[offset++] = size; - memcpy(&buf[offset], gseData->goID, size); - offset += size; + buf[offset++] = 0x83; + size = strlen((const char *) gseData->goID); + buf[offset++] = size; + memcpy(&buf[offset], gseData->goID, size); + offset += size; - buf[offset++] = 0x84; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t)); - setTimestamp(&gseData->t); - memcpy(&buf[offset], &gseData->t, BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t)); - offset += BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t); + buf[offset++] = 0x84; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t)); + setTimestamp(&gseData->t); + memcpy(&buf[offset], &gseData->t, BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t)); + offset += BER_GET_LENGTH_CTYPE_TIMESTAMP(&gseData->t); - buf[offset++] = 0x85; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->stNum)); - offset += ber_encode_integer(&buf[offset], &gseData->stNum, SV_GET_LENGTH_INT32U); + buf[offset++] = 0x85; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->stNum)); + offset += ber_encode_integer(&buf[offset], &gseData->stNum, SV_GET_LENGTH_INT32U); - buf[offset++] = 0x86; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->sqNum)); - offset += ber_encode_integer(&buf[offset], &gseData->sqNum, SV_GET_LENGTH_INT32U); + buf[offset++] = 0x86; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->sqNum)); + offset += ber_encode_integer(&buf[offset], &gseData->sqNum, SV_GET_LENGTH_INT32U); - buf[offset++] = 0x87; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->test)); - offset += ber_encode_integer(&buf[offset], &gseData->test, SV_GET_LENGTH_BOOLEAN); + buf[offset++] = 0x87; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->test)); + offset += ber_encode_integer(&buf[offset], &gseData->test, SV_GET_LENGTH_BOOLEAN); - buf[offset++] = 0x88; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->confRev)); - offset += ber_encode_integer(&buf[offset], &gseData->confRev, SV_GET_LENGTH_INT32U); + buf[offset++] = 0x88; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->confRev)); + offset += ber_encode_integer(&buf[offset], &gseData->confRev, SV_GET_LENGTH_INT32U); - buf[offset++] = 0x89; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->ndsCom)); - offset += ber_encode_integer(&buf[offset], &gseData->ndsCom, SV_GET_LENGTH_BOOLEAN); + buf[offset++] = 0x89; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_BOOLEAN(&gseData->ndsCom)); + offset += ber_encode_integer(&buf[offset], &gseData->ndsCom, SV_GET_LENGTH_BOOLEAN); - buf[offset++] = 0x8A; - offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->numDatSetEntries)); - offset += ber_encode_integer(&buf[offset], &gseData->numDatSetEntries, SV_GET_LENGTH_INT32U); + buf[offset++] = 0x8A; + offset += encodeLength(&buf[offset], BER_GET_LENGTH_CTYPE_INT32U(&gseData->numDatSetEntries)); + offset += ber_encode_integer(&buf[offset], &gseData->numDatSetEntries, SV_GET_LENGTH_INT32U); - buf[offset++] = 0xAB; - offset += encodeLength(&buf[offset], (gseData->getDatasetLength)()); - offset += (gseData->encodeDataset)(&buf[offset]); + buf[offset++] = 0xAB; + offset += encodeLength(&buf[offset], (gseData->getDatasetLength)()); + offset += (gseData->encodeDataset)(&buf[offset]); - // assume network interface, such as WinPcap, generates CRC bytes + // assume network interface, such as WinPcap, generates CRC bytes - return offset; + return offset; }