/*=============================================================================| | PROJECT SNAP7 1.3.0 | |==============================================================================| | Copyright (C) 2013, 2015 Davide Nardella | | All rights reserved. | |==============================================================================| | SNAP7 is free software: you can redistribute it and/or modify | | it under the terms of the Lesser GNU General Public License as published by | | the Free Software Foundation, either version 3 of the License, or | | (at your option) any later version. | | | | It means that you can distribute your commercial software linked with | | SNAP7 without the requirement to distribute the source code of your | | application and without the requirement that your application be itself | | distributed under LGPL. | | | | SNAP7 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 | | Lesser GNU General Public License for more details. | | | | You should have received a copy of the GNU General Public License and a | | copy of Lesser GNU General Public License along with Snap7. | | If not, see http://www.gnu.org/licenses/ | |=============================================================================*/ #ifndef s7_isotcp_h #define s7_isotcp_h //--------------------------------------------------------------------------- #include "snap_msgsock.h" //--------------------------------------------------------------------------- #pragma pack(1) #define isoTcpVersion 3 // RFC 1006 #define isoTcpPort 102 // RFC 1006 #define isoInvalidHandle 0 #define MaxTSAPLength 16 // Max Lenght for Src and Dst TSAP #define MaxIsoFragments 64 // Max fragments #define IsoPayload_Size 4096 // Iso telegram Buffer size #define noError 0 const longword errIsoMask = 0x000F0000; const longword errIsoBase = 0x0000FFFF; const longword errIsoConnect = 0x00010000; // Connection error const longword errIsoDisconnect = 0x00020000; // Disconnect error const longword errIsoInvalidPDU = 0x00030000; // Bad format const longword errIsoInvalidDataSize = 0x00040000; // Bad Datasize passed to send/recv : buffer is invalid const longword errIsoNullPointer = 0x00050000; // Null passed as pointer const longword errIsoShortPacket = 0x00060000; // A short packet received const longword errIsoTooManyFragments = 0x00070000; // Too many packets without EoT flag const longword errIsoPduOverflow = 0x00080000; // The sum of fragments data exceded maximum packet size const longword errIsoSendPacket = 0x00090000; // An error occurred during send const longword errIsoRecvPacket = 0x000A0000; // An error occurred during recv const longword errIsoInvalidParams = 0x000B0000; // Invalid TSAP params const longword errIsoResvd_1 = 0x000C0000; // Unassigned const longword errIsoResvd_2 = 0x000D0000; // Unassigned const longword errIsoResvd_3 = 0x000E0000; // Unassigned const longword errIsoResvd_4 = 0x000F0000; // Unassigned const longword ISO_OPT_TCP_NODELAY = 0x00000001; // Disable Nagle algorithm const longword ISO_OPT_INSIDE_MTU = 0x00000002; // Max packet size < MTU ethernet card // TPKT Header - ISO on TCP - RFC 1006 (4 bytes) typedef struct{ u_char Version; // Always 3 for RFC 1006 u_char Reserved; // 0 u_char HI_Lenght; // High part of packet lenght (entire frame, payload and TPDU included) u_char LO_Lenght; // Low part of packet lenght (entire frame, payload and TPDU included) } TTPKT; // Packet length : min 7 max 65535 typedef struct { u_char PduSizeCode; u_char PduSizeLen; u_char PduSizeVal; u_char TSAP[245]; // We don't know in advance these fields.... } TCOPT_Params ; // PDU Type constants - ISO 8073, not all are mentioned in RFC 1006 // For our purposes we use only those labeled with ** // These constants contains 4 low bit order 0 (credit nibble) // // $10 ED : Expedited Data // $20 EA : Expedited Data Ack // $40 UD : CLTP UD // $50 RJ : Reject // $70 AK : Ack data // ** $80 DR : Disconnect request (note : S7 doesn't use it) // ** $C0 DC : Disconnect confirm (note : S7 doesn't use it) // ** $D0 CC : Connection confirm // ** $E0 CR : Connection request // ** $F0 DT : Data // // COTP Header for CONNECTION REQUEST/CONFIRM - DISCONNECT REQUEST/CONFIRM typedef struct { u_char HLength; // Header length : initialized to 6 (length without params - 1) // descending classes that add values in params field must update it. u_char PDUType; // 0xE0 Connection request // 0xD0 Connection confirm // 0x80 Disconnect request // 0xDC Disconnect confirm u_short DstRef; // Destination reference : Always 0x0000 u_short SrcRef; // Source reference : Always 0x0000 u_char CO_R; // If the telegram is used for Connection request/Confirm, // the meaning of this field is CLASS+OPTION : // Class (High 4 bits) + Option (Low 4 bits) // Class : Always 4 (0100) but is ignored in input (RFC States this) // Option : Always 0, also this in ignored. // If the telegram is used for Disconnect request, // the meaning of this field is REASON : // 1 Congestion at TSAP // 2 Session entity not attached to TSAP // 3 Address unknown (at TCP connect time) // 128+0 Normal disconnect initiated by the session // entity. // 128+1 Remote transport entity congestion at connect // request time // 128+3 Connection negotiation failed // 128+5 Protocol Error // 128+8 Connection request refused on this network // connection // Parameter data : depending on the protocol implementation. // ISO 8073 define several type of parameters, but RFC 1006 recognizes only // TSAP related parameters and PDU size. See RFC 0983 for more details. TCOPT_Params Params; /* Other params not used here, list only for completeness ACK_TIME = 0x85, 1000 0101 Acknowledge Time RES_ERROR = 0x86, 1000 0110 Residual Error Rate PRIORITY = 0x87, 1000 0111 Priority TRANSIT_DEL = 0x88, 1000 1000 Transit Delay THROUGHPUT = 0x89, 1000 1001 Throughput SEQ_NR = 0x8A, 1000 1010 Subsequence Number (in AK) REASSIGNMENT = 0x8B, 1000 1011 Reassignment Time FLOW_CNTL = 0x8C, 1000 1100 Flow Control Confirmation (in AK) TPDU_SIZE = 0xC0, 1100 0000 TPDU Size SRC_TSAP = 0xC1, 1100 0001 TSAP-ID / calling TSAP ( in CR/CC ) DST_TSAP = 0xC2, 1100 0010 TSAP-ID / called TSAP CHECKSUM = 0xC3, 1100 0011 Checksum VERSION_NR = 0xC4, 1100 0100 Version Number PROTECTION = 0xC5, 1100 0101 Protection Parameters (user defined) OPT_SEL = 0xC6, 1100 0110 Additional Option Selection PROTO_CLASS = 0xC7, 1100 0111 Alternative Protocol Classes PREF_MAX_TPDU_SIZE = 0xF0, 1111 0000 INACTIVITY_TIMER = 0xF2, 1111 0010 ADDICC = 0xe0 1110 0000 Additional Information on Connection Clearing */ } TCOTP_CO ; typedef TCOTP_CO *PCOTP_CO; // COTP Header for DATA EXCHANGE typedef struct { u_char HLength; // Header length : 3 for this header u_char PDUType; // 0xF0 for this header u_char EoT_Num; // EOT (bit 7) + PDU Number (bits 0..6) // EOT = 1 -> End of Trasmission Packet (This packet is complete) // PDU Number : Always 0 } TCOTP_DT; typedef TCOTP_DT *PCOTP_DT; // Info part of a PDU, only common parts. We use it to check the consistence // of a telegram regardless of it's nature (control or data). typedef struct { TTPKT TPKT; // TPKT Header // Common part of any COTP u_char HLength; // Header length : 3 for this header u_char PDUType; // Pdu type } TIsoHeaderInfo ; typedef TIsoHeaderInfo *PIsoHeaderInfo; // PDU Type consts (Code + Credit) const byte pdu_type_CR = 0xE0; // Connection request const byte pdu_type_CC = 0xD0; // Connection confirm const byte pdu_type_DR = 0x80; // Disconnect request const byte pdu_type_DC = 0xC0; // Disconnect confirm const byte pdu_type_DT = 0xF0; // Data transfer const byte pdu_EoT = 0x80; // End of Trasmission Packet (This packet is complete) const longword DataHeaderSize = sizeof(TTPKT)+sizeof(TCOTP_DT); const longword IsoFrameSize = IsoPayload_Size+DataHeaderSize; typedef struct { TTPKT TPKT; // TPKT Header TCOTP_CO COTP; // COPT Header for CONNECTION stuffs } TIsoControlPDU; typedef TIsoControlPDU *PIsoControlPDU; typedef u_char TIsoPayload[IsoPayload_Size]; typedef struct { TTPKT TPKT; // TPKT Header TCOTP_DT COTP; // COPT Header for DATA EXCHANGE TIsoPayload Payload; // Payload } TIsoDataPDU ; typedef TIsoDataPDU *PIsoDataPDU; typedef TIsoPayload *PIsoPayload; typedef enum { pkConnectionRequest, pkDisconnectRequest, pkEmptyFragment, pkInvalidPDU, pkUnrecognizedType, pkValidData } TPDUKind ; #pragma pack() void ErrIsoText(int Error, char *Msg, int len); class TIsoTcpSocket : public TMsgSocket { private: TIsoControlPDU FControlPDU; int IsoMaxFragments; // max fragments allowed for an ISO telegram // Checks the PDU format int CheckPDU(void *pPDU, u_char PduTypeExpected); // Receives the next fragment int isoRecvFragment(void *From, int Max, int &Size, bool &EoT); protected: TIsoDataPDU PDU; int SetIsoError(int Error); // Builds the control PDU starting from address properties virtual int BuildControlPDU(); // Calcs the PDU size int PDUSize(void *pPDU); // Parses the connection request PDU to extract TSAP and PDU size info virtual void IsoParsePDU(TIsoControlPDU PDU); // Confirms the connection, override this method for special pourpose // By default it checks the PDU format and resend it changing the pdu type int IsoConfirmConnection(u_char PDUType); void ClrIsoError(); virtual void FragmentSkipped(int Size); public: word SrcTSap; // Source TSAP word DstTSap; // Destination TSAP word SrcRef; // Source Reference word DstRef; // Destination Reference int IsoPDUSize; int LastIsoError; //-------------------------------------------------------------------------- TIsoTcpSocket(); ~TIsoTcpSocket(); // HIGH Level functions (work on payload hiding the underlying protocol) // Connects with a peer, the connection PDU is automatically built starting from address scheme (see below) int isoConnect(); // Disconnects from a peer, if OnlyTCP = true, only a TCP disconnect is performed, // otherwise a disconnect PDU is built and send. int isoDisconnect(bool OnlyTCP); // Sends a buffer, a valid header is created int isoSendBuffer(void *Data, int Size); // Receives a buffer int isoRecvBuffer(void *Data, int & Size); // Exchange cycle send->receive int isoExchangeBuffer(void *Data, int & Size); // A PDU is ready (at least its header) to be read bool IsoPDUReady(); // Same as isoSendBuffer, but the entire PDU has to be provided (in any case a check is performed) int isoSendPDU(PIsoDataPDU Data); // Same as isoRecvBuffer, but it returns the entire PDU, automatically enques the fragments int isoRecvPDU(PIsoDataPDU Data); // Same as isoExchangeBuffer, but the entire PDU has to be provided (in any case a check is performed) int isoExchangePDU(PIsoDataPDU Data); // Peeks an header info to know which kind of telegram is incoming void IsoPeek(void *pPDU, TPDUKind &PduKind); }; #endif // s7_isotcp_h