272 lines
12 KiB
C
272 lines
12 KiB
C
|
/*=============================================================================|
|
||
|
| 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
|