2024-03-19 17:45:12 +08:00

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