blob: 28ef97f625775e71399ffa18536dd8e816fe9157 [file] [log] [blame]
///////////////////////////////////////////////////////////////////////////////
// //
// Copyright (c) 2000-2019 Ericsson Telecom AB //
// //
// All rights reserved. This program and the accompanying materials //
// are made available under the terms of the Eclipse Public License v2.0 //
// which accompanies this distribution, and is available at //
// https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html //
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
// Module: EPTF_CLL_TransportIPL2_Functions
//
// Purpose:
// Implementation of UDP transport over IP Layer 2 (LANL2 test port).
//
// Module Parameters:
// tsp_EPTF_TransportIPL2_enableLogging - *boolean*
// tsp_EPTF_TransportIPL2_loggingComponentMask - *charstring*
// tsp_EPTF_TransportIPL2_enableStats - *boolean*
// tsp_EPTF_TransportIPL2_multipleInterfacesMode - *boolean*
// tsp_EPTF_TransportIPL2_loopbackInterface - *charstring*
// tsp_EPTF_TransportIPL2_loopbackInterfaceAddress - *charstring*
// tsp_EPTF_TransportIPL2_loopbackInterfaceMask *charstring*
// tsp_EPTF_TransportIPL2_dstMacAddr - <OCT6>
// tsp_EPTF_TransportIPL2_srcMacAddr - <OCT6>
// tsp_EPTF_TransportIPL2_MTU - *integer*
// tsp_EPTF_TransportIPL2_ipFragmentRemoveTimer - *float*
//
// Module depends on:
// <EPTF_CLL_Base_Functions>
// <EPTF_CLL_Common_Definitions>
// <EPTF_CLL_Common_Functions>
// <EPTF_CLL_FBQ_Definitions>
// <EPTF_CLL_FBQ_Functions>
// <EPTF_CLL_HashMapOct2Int_Functions>
// <EPTF_CLL_HashMapStr2Int_Functions>
// <EPTF_CLL_Logging_Definitions>
// <EPTF_CLL_Logging_Functions>
// <EPTF_CLL_RBTScheduler_Functions>
// <EPTF_CLL_Scheduler_Definitions>
// <EPTF_CLL_Transport_CommonDefinitions>
// <EPTF_CLL_TransportIPL2_Definitions>
// <EPTF_CLL_Variable_Definitions>
// <EPTF_CLL_Variable_Functions>
// <EPTF_CLL_Semaphore_Functions>
// <General_Types>
// <IP_Types>
// <LANL2asp_Types>
// <TCCConversion_Functions>
// <TCCInterface_Functions>
// <Socket_API_Definitions>
// <EPTF_CLL_HashMap_Functions>
//
// Current Owner:
// EJNOSVN, EGBOTAT
//
// Last Review Date:
// -
//
// Detailed Comments:
// -
//
// Public API:
// <f_EPTF_Transport_init>
// <f_EPTF_Transport_registerMsgLenCallback4LGenType>
// <f_EPTF_Transport_setUserData>
// <f_EPTF_Transport_getUserData>
// <f_EPTF_TransportIPL2_setMsgHandler>
// <f_EPTF_Transport_send>
// <f_EPTF_Transport_sendTo>
// <f_EPTF_Transport_listen>
// <f_EPTF_Transport_connect>
// <f_EPTF_TransportIPL2_connectById>
// <f_EPTF_Transport_close>
// <f_EPTF_TransportIPL2_getLocalHost>
// <f_EPTF_TransportIPL2_getLocalPort>
// <f_EPTF_Transport_getProto>
// <f_EPTF_Transport_registerMsgCallback>
// <f_EPTF_Transport_getLocalAddress>
// <f_EPTF_Transport_getRemoteAddress>
// <f_EPTF_Transport_setUpInterfaces>
// <f_EPTF_Transport_appendInterfaces>
// <f_EPTF_Transport_setDownInterfaces>
// <f_EPTF_SegmentBuffer_initBuffer>
// <f_EPTF_SegmentBuffer_setFinalSegmentOffset>
// <f_EPTF_SegmentBuffer_insertSegment>
// <f_EPTF_SegmentBuffer_getContinousLen>
// <f_EPTF_SegmentBuffer_getNonContinousLen>
// <f_EPTF_SegmentBuffer_getBuffer>
// <f_EPTF_SegmentBuffer_truncBuffer>
// <f_EPTF_TransportIPL2_getNextHop>
// <f_EPTF_TransportIPL2_normalizeRoutingTable>
// <f_EPTF_TransportIPL2_getHostRoutingTable>
// <f_EPTF_TransportIPL2_getRouteTable>
// <f_EPTF_TransportIPL2_setRouteTable>
// <f_EPTF_TransportIPL2_addRoute>
// <f_EPTF_TransportIPL2_ARP_getMac>
//
///////////////////////////////////////////////////////////
module EPTF_CLL_TransportIPL2_Functions {
//=========================================================================
// Import Part
//=========================================================================
import from EPTF_CLL_Base_Functions all;
import from EPTF_CLL_Common_Definitions all;
import from EPTF_CLL_Common_Functions all;
import from EPTF_CLL_FBQ_Definitions all;
import from EPTF_CLL_FBQ_Functions all;
import from EPTF_CLL_HashMapOct2Int_Functions all;
import from EPTF_CLL_HashMapStr2Int_Functions all;
import from EPTF_CLL_Logging_Definitions all;
import from EPTF_CLL_Logging_Functions all;
import from EPTF_CLL_RBTScheduler_Functions all;
import from EPTF_CLL_Scheduler_Definitions all;
import from EPTF_CLL_Transport_CommonDefinitions all;
import from EPTF_CLL_TransportIPL2_Definitions all;
import from EPTF_CLL_Variable_Definitions all;
import from EPTF_CLL_Variable_Functions all;
import from EPTF_CLL_Semaphore_Functions all;
import from General_Types all;
import from IP_Types all;
import from LANL2asp_Types all;
import from LANL2asp_PortType all;
import from TCCConversion_Functions all;
import from TCCInterface_Functions all;
import from Socket_API_Definitions all;
import from EPTF_CLL_Buffer_Functions all;
import from EPTF_CLL_HashMap_Functions all;
friend module EPTF_Transport_Test_Testcases, EPTF_Transport_Test_perfTestcases;
friend module EPTF_CLL_Transport_Functions;
//=========================================================================
// Module parameters
//========================================================================
modulepar boolean tsp_EPTF_TransportIPL2_enableLogging := false;
modulepar charstring tsp_EPTF_TransportIPL2_loggingComponentMask := "EPTF_TransportIPL2";
modulepar boolean tsp_EPTF_TransportIPL2_enableStats := true;
modulepar boolean tsp_EPTF_TransportIPL2_multipleInterfacesMode := true; // note: if this is true, IPL2_PCO will be set to multiple interfaces mode automatically as well
// in multiple interfaces mode, the interface information parameters passed in to the init function will be used to open interfaces
// additional interfaces can be later configured using appendInterfaces()
modulepar boolean tsp_EPTF_TransportIPL2_openLoopbackInterface := false; // whether to open the loopback interface automatically (without being added in setUpInterfaces or appendInterfaces!) in multiple interfaces mode
modulepar charstring tsp_EPTF_TransportIPL2_loopbackInterface := "lo"; // loopback interface name, used only in multiple interfaces mode
modulepar charstring tsp_EPTF_TransportIPL2_loopbackInterfaceAddress := "127.0.0.0"; // default subnet address for loopback interface
modulepar charstring tsp_EPTF_TransportIPL2_loopbackInterfaceMask := "255.0.0.0"; // default subnet mask for loopback interface
modulepar OCT6 tsp_EPTF_TransportIPL2_dstMacAddr := cg_EPTF_TransportIPL2_NullMACAddress;
modulepar OCT6 tsp_EPTF_TransportIPL2_srcMacAddr := cg_EPTF_TransportIPL2_NullMACAddress; // needed in single interface mode
modulepar integer tsp_EPTF_TransportIPL2_MTU := 1500; // Maximum Transmission Unit
modulepar boolean tsp_EPTF_TransportIPL2_enableIpAddressFilter := false;
modulepar float tsp_EPTF_TransportIPL2_ipFragmentRemoveTimer := 0.01;
modulepar integer tsp_EPTF_TransportIPL2_tcpMSS := c_EPTF_TransportIPL2_tcpDefaultMSS; // Maximum Segment Size for TCP
modulepar float tsp_EPTF_TransportIPL2_tcpMSL := c_EPTF_TransportIPL2_tcpDefaultMSL; // Maximum Segment Lifetime for TCP
modulepar boolean tsp_EPTF_TransportIPL2_tcpAllowHalfClose := true; // allow half-closed sockets (the standard allows it)
modulepar integer tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize := 4096;
modulepar float tsp_EPTF_TransportIPL2_tcpMaxAckDelay := 0.2;
modulepar float tsp_EPTF_TransportIPL2_tcpConnectionTimeout := c_EPTF_TransportIPL2_tcpDefaultMSL;
modulepar float tsp_EPTF_TransportIPL2_tcpMinRetransmitTime := 0.5;
modulepar float tsp_EPTF_TransportIPL2_tcpMaxRetransmitTime := 30.0;
modulepar boolean tsp_EPTF_TransportIPL2_tcpCalculateRTT := true;
modulepar float tsp_EPTF_TransportIPL2_tcpSmoothAlpha := 0.8; // used for calculating smoothd RTT, see RFC 793
modulepar float tsp_EPTF_TransportIPL2_tcpSmoothBeta := 1.3; // used for calculating retransmit time from smoothd RTT, see RFC 793
modulepar float tsp_EPTF_TransportIPL2_arpMaxWaitTime := 10.0;
modulepar boolean tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer := false; // sendFifo is an octetstring of CLL_Buffer?
modulepar boolean tsp_EPTF_SegmentBuffer_useCLLBuffer := false; // SegmentBuffer uses octetstring to store the continous part or CLL_Buffer?
external function f_EPTF_TransportIPL2_encodeIP(in IPv4_packet pdu) return octetstring;
external function f_EPTF_TransportIPL2_decodeIP(in octetstring data, out IPv4_packet pdu) return boolean;
external function f_EPTF_TransportIPL2_encodeUDPWithChecksum(
in octetstring srcaddr,
in integer srcpotr,
in octetstring dstaddr,
in integer dstport,
in octetstring payload) return octetstring;
external function f_EPTF_TransportIPL2_decodeUDPWithChecksum(
in octetstring srcaddr,
in octetstring dstaddr,
in octetstring udppdu,
out integer srcpotr,
out integer dstport,
out octetstring payload) return boolean; // false if decoding or checksum check fails, true on valid payload
external function f_EPTF_TransportIPL2_encodeTCPWithChecksum(
in octetstring srcaddr,
in octetstring dstaddr,
in EPTF_TransportIPL2_PDU_TCP pdu) return octetstring;
external function f_EPTF_TransportIPL2_verifyUDPChecksum(
in octetstring srcaddr,
in octetstring dstaddr,
in octetstring tcppdu) return boolean; // false if checksum verification fails
external function f_EPTF_TransportIPL2_verifyTCPChecksum(
in octetstring srcaddr,
in octetstring dstaddr,
in octetstring tcppdu) return boolean; // false if checksum verification fails
external function f_EPTF_TransportIPL2_decodeTCP(
in octetstring tcppdu,
out EPTF_TransportIPL2_PDU_TCP pdu) return boolean; // false if decoding fails, true on valid payload
// bitwise logical operations for integers
external function f_EPTF_and4i(in integer i1, in integer i2) return integer;
external function f_EPTF_or4i(in integer i1, in integer i2) return integer;
external function f_EPTF_xor4i(in integer i1, in integer i2) return integer;
external function f_EPTF_not4i(in integer i) return integer;
external function f_EPTF_intShiftLeft(in integer i, in integer bits) return integer;
external function f_EPTF_intShiftRight(in integer i, in integer bits) return integer;
// integer modulo32 operations
external function f_EPTF_mod32Add(in integer i1, in integer i2) return integer;
external function f_EPTF_mod32Sub(in integer i1, in integer i2) return integer;
external function f_EPTF_mod32Less(in integer i1, in integer i2) return boolean; // returns true if i1 < i2
external function f_EPTF_mod32LessOrEqual(in integer i1, in integer i2) return boolean;
external function f_EPTF_mod32Greater(in integer i1, in integer i2) return boolean; // returns true if i1 < i2
external function f_EPTF_mod32GreaterOrEqual(in integer i1, in integer i2) return boolean;
///////////////////////////////////////////////////////////
// Altstep: as_EPTF_TransportIPL2_defaultReceive
//
// Purpose:
// Default altstep to handle receiving messages
///////////////////////////////////////////////////////////
private altstep as_EPTF_TransportIPL2_defaultReceive ()
runs on EPTF_TransportIPL2_CT
{
var ASP_LANL2 vl_message;
var ASP_v2_LANL2 vl_message_v2;
var ASP_LANL2_Error vl_error;
var ASP_v2_LANL2_Error vl_error_v2;
[] IPL2_PCO.receive (ASP_LANL2 : ?) -> value vl_message
{
f_EPTF_TransportIPL2_handleIncomingPacket(-1, vl_message.type_field, vl_message.payload);
repeat;
}
[] IPL2_PCO.receive (ASP_v2_LANL2 : ?) -> value vl_message_v2
{
f_EPTF_TransportIPL2_handleIncomingPacket(vl_message_v2.interface_id, vl_message_v2.type_field, vl_message_v2.payload);
repeat;
}
[] IPL2_PCO.receive (ASP_LANL2_Error : ?) -> value vl_error
{
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": Got error ASP from IPL2_PCO: " & log2str(vl_error));
}
repeat;
}
[] IPL2_PCO.receive (ASP_v2_LANL2_Error : ?) -> value vl_error_v2
{
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": Got error ASP from IPL2_PCO: " & log2str(vl_error_v2));
}
repeat;
}
[] IPL2_PCO.receive { repeat; }
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_handleIncomingPacket
//
// Purpose:
// Function to handle received ethernet packets
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_handleIncomingPacket(
integer interface_id,
// octetstring eth_dst_addr,
// octetstring eth_src_addr,
octetstring type_field,
octetstring payload)
runs on EPTF_TransportIPL2_CT
{
if (type_field == int2oct(c_ip_gre_proto_ipv4, 2)) {
// if we have an ip packet, decode it.
if(tsp_EPTF_TransportIPL2_enableIpAddressFilter) {
// This code is used to filter incoming packets based on the destination IP,
// IP addresses that we are listening on, are added to this filter in newLocalPort.
// Without this filter, if more than one LGen is running, they all do the
// IP and UDP decode, checksum check and hashmap search to see if the packet is for the given LGen.
var octetstring vl_destIp := substr(payload, 16, 4);
if(not f_EPTF_TransportIPL2_hasIp(vl_destIp)) {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": IP address "&f_EPTF_TransportIPL2_ip2str(vl_destIp)&" not in filter. Dropping message.");
}
return;
}
}
var octetstring ck1 := payload[10];
var octetstring ck2 := payload[11];
payload[10] := '00'O;
payload[11] := '00'O;
var octetstring chksum := f_IPv4_checksum( payload );
if (ck1 == chksum[0] and ck2 == chksum[1]) {
// var IPv4_packet vl_ipv4Message := f_IPv4_dec(data := payload);
var IPv4_packet vl_ipv4Message;
if(f_EPTF_TransportIPL2_decodeIP(payload, vl_ipv4Message)) {
f_EPTF_TransportIPL2_incomingIPFragment(
vl_ipv4Message.header.srcaddr,
vl_ipv4Message.header.dstaddr,
vl_ipv4Message.header.proto,
vl_ipv4Message.header.id,
vl_ipv4Message.payload,
vl_ipv4Message.header.foffset * c_EPTF_TransportIPL2_ipFragmentOffsetScale,
vl_ipv4Message.header.mfrag != '1'B
);
} else {
if (vl_ipv4Message.header.proto == c_ip_proto_udp) {
f_EPTF_TransportIPL2_incomingUdpPacket(
vl_ipv4Message.header.srcaddr,
vl_ipv4Message.header.dstaddr,
vl_ipv4Message.payload);
} else if (vl_ipv4Message.header.proto == c_ip_proto_tcp) {
f_EPTF_TransportIPL2_incomingTcpSegment(
vl_ipv4Message.header.srcaddr,
vl_ipv4Message.header.dstaddr,
vl_ipv4Message.payload);
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": Unsupported protocol in IP datagram: " & int2str(vl_ipv4Message.header.proto));
}
}
}
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": incorrect IP checksum "&oct2str(chksum)&" should be " & oct2str(ck1) & oct2str(ck2));
}
}
} else if (type_field == int2oct(c_ip_gre_proto_arp, 2)) {
f_EPTF_TransportIPL2_debug(%definitionId&": ARP received");
var ARP_packet vl_arpPacket;
f_EPTF_TransportIPL2_decodeARP(payload, vl_arpPacket)
if (f_EPTF_TransportIPL2_hasIp(vl_arpPacket.targetIPAddr)) {
if (vl_arpPacket.arpType == cg_EPTF_TransportIPL2_ARP_TypeReply) {
// Handle ARP reply message
if (f_EPTF_TransportIPL2_addMacToGwArpDB(vl_arpPacket)
or f_EPTF_TransportIPL2_addMacToIpArpDB(vl_arpPacket)) {
if (c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": MAC found for IP: "&log2str(vl_arpPacket));
}
}
} else if (vl_arpPacket.arpType == cg_EPTF_TransportIPL2_ARP_TypeRequest) {
// Handle ARP request message
var EPTF_TransportIPL2_MACAddress vl_srcMac := tsp_EPTF_TransportIPL2_srcMacAddr;
var integer vl_interfaceIdx := f_EPTF_TransportIPL2_getInterfaceIdx(interface_id);
if(vl_interfaceIdx >= 0 and ispresent(v_EPTF_TransportIPL2_ethernetInterfaceList[vl_interfaceIdx].macAddress)) {
vl_srcMac := v_EPTF_TransportIPL2_ethernetInterfaceList[vl_interfaceIdx].macAddress;
}
f_EPTF_TransportIPL2_ARP_sendMACReplyForIp(
vl_interfaceIdx,
vl_arpPacket.targetIPAddr,
vl_srcMac,
vl_arpPacket.senderIPAddr,
vl_arpPacket.senderMACAddr);
if (c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": Send ARP reply to "&
f_EPTF_TransportIPL2_ip2str(vl_arpPacket.senderIPAddr));
}
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": invalid type in ARP message: "&log2str(vl_arpPacket));
}
}
}
}else if(type_field == int2oct(oct2int('8100'O), 2)){//VLAN packet
payload := substr(payload, 2, lengthof(payload)-2);
while(substr(payload, 0, 2) == c_EPTF_TransportIPL2_VLAN_TPID)
{
payload := substr(payload, 4, lengthof(payload)-4 );
}
f_EPTF_TransportIPL2_handleIncomingPacket(interface_id, substr(payload, 0, 2), substr(payload, 2, lengthof(payload)-2));
}else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": not an IP packet, type_field: "&log2str(type_field));
}
}
}
private function f_EPTF_TransportIPL2_getInterfaceIdx(in integer pl_interface_id) runs on EPTF_TransportIPL2_CT return integer {
for(var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); i:=i+1) {
if (v_EPTF_TransportIPL2_ethernetInterfaceList[i].interfaceId == pl_interface_id) {
return i;
}
}
return -1; // not found
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_initStatistics
//
// Purpose:
// Function to initialize the statistics
//
// Parameters:
// -
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_initStatistics ()
runs on EPTF_TransportIPL2_CT
{
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_incMessageName, 0, v_EPTF_TransportIPL2_incStats[c_EPTF_TransportIPL2_incMessage]);
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_incConnOpenedName, 0, v_EPTF_TransportIPL2_incStats[c_EPTF_TransportIPL2_incConnOpened]);
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_incConnClosedName, 0, v_EPTF_TransportIPL2_incStats[c_EPTF_TransportIPL2_incConnClosed]);
v_EPTF_TransportIPL2_incStatCounters[c_EPTF_TransportIPL2_incMessage] := 0;
v_EPTF_TransportIPL2_incStatCounters[c_EPTF_TransportIPL2_incConnOpened] := 0;
v_EPTF_TransportIPL2_incStatCounters[c_EPTF_TransportIPL2_incConnClosed] := 0;
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_outMessageName, 0, v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outMessage]);
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_outCloseName, 0, v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outClose]);
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_outConnectName, 0, v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outConnect]);
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_outListenName, 0, v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outListen]);
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outMessage] := 0;
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outClose] := 0;
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outConnect] := 0;
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outListen] := 0;
f_EPTF_Var_newInt(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_nofErrorsName,0, v_EPTF_TransportIPL2_nofErrors);
f_EPTF_Var_newCharstring(c_EPTF_TransportIPL2_varNamePrefix&c_EPTF_TransportIPL2_lastErrorStringName, "", v_EPTF_TransportIPL2_lastErrorString);
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_resetStatistics
//
// Purpose:
// Function to reset the statistics
//
// Parameters:
// -
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_resetStatistics ()
runs on EPTF_TransportIPL2_CT
{
if(tsp_EPTF_TransportIPL2_enableStats) {
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_incStats[c_EPTF_TransportIPL2_incMessage], {intVal:=0});
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_incStats[c_EPTF_TransportIPL2_incConnOpened], {intVal:=0});
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_incStats[c_EPTF_TransportIPL2_incConnClosed], {intVal:=0});
v_EPTF_TransportIPL2_incStatCounters[c_EPTF_TransportIPL2_incMessage] := 0;
v_EPTF_TransportIPL2_incStatCounters[c_EPTF_TransportIPL2_incConnOpened] := 0;
v_EPTF_TransportIPL2_incStatCounters[c_EPTF_TransportIPL2_incConnClosed] := 0;
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outMessage], {intVal:=0});
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outClose], {intVal:=0});
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outConnect], {intVal:=0});
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_outStats[c_EPTF_TransportIPL2_outListen], {intVal:=0});
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outMessage] := 0;
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outClose] := 0;
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outConnect] := 0;
v_EPTF_TransportIPL2_outStatCounters[c_EPTF_TransportIPL2_outListen] := 0;
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_nofErrors, {intVal:=0});
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_lastErrorString, {charstringVal:=""});
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_updateIncomingStatistics
//
// Purpose:
// Function to update the incoming statistics
//
// Parameters:
// pl_idx - *in* - *integer* - the index of the statistic
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_updateIncomingStatistics (
in integer pl_idx)
runs on EPTF_TransportIPL2_CT
{
if(tsp_EPTF_TransportIPL2_enableStats) {
var integer vl_varIdx := v_EPTF_TransportIPL2_incStats[pl_idx];
v_EPTF_TransportIPL2_incStatCounters[pl_idx] := v_EPTF_TransportIPL2_incStatCounters[pl_idx] + 1;
f_EPTF_Var_setContent(vl_varIdx, {intVal := v_EPTF_TransportIPL2_incStatCounters[pl_idx]});
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_increaseErrors
//
// Purpose:
// Function to increase the number of the errors
//
// Parameters:
// -
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_increaseErrors ()
runs on EPTF_TransportIPL2_CT
{
var EPTF_Var_DirectContent vl_prevContent;
f_EPTF_Var_getContent(v_EPTF_TransportIPL2_nofErrors, vl_prevContent);
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_nofErrors, {intVal := vl_prevContent.intVal + 1 });
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_updateOutgoingStatistics
//
// Purpose:
// Function to update the outgoing statistics
//
// Parameters:
// pl_idx - *in* - *integer* - the index of the statistic
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_updateOutgoingStatistics (
in integer pl_idx)
runs on EPTF_TransportIPL2_CT
{
if(tsp_EPTF_TransportIPL2_enableStats) {
var integer vl_varIdx := v_EPTF_TransportIPL2_outStats[pl_idx];
v_EPTF_TransportIPL2_outStatCounters[pl_idx] := v_EPTF_TransportIPL2_outStatCounters[pl_idx] + 1;
f_EPTF_Var_setContent(vl_varIdx, {intVal := v_EPTF_TransportIPL2_outStatCounters[pl_idx]});
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_initExtendedComponents
//
// Purpose:
// Function to initialize extended components
//
// Parameters:
// pl_selfName - *in* - *charstring* - The name of the LGen
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_initExtendedComponents (
in charstring pl_selfName)
runs on EPTF_TransportIPL2_CT
{
f_EPTF_Base_init_CT(pl_selfName);
f_EPTF_FBQ_init_CT(pl_selfName);
f_EPTF_Logging_init_CT(pl_selfName);
f_EPTF_Var_init_CT(pl_selfName);
f_EPTF_Scheduler_init_CT(pl_selfName);
f_EPTF_Semaphore_init_CT(pl_selfName);
f_EPTF_SegmentBuffer_init_CT(pl_selfName);
}
private function f_EPTF_TransportIPL2_initLoopbackInterface()
runs on EPTF_TransportIPL2_CT
{
for(var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); i:=i+1) {
if(v_EPTF_TransportIPL2_ethernetInterfaceList[i].name == tsp_EPTF_TransportIPL2_loopbackInterface) {
v_EPTF_TransportIPL2_loopbackIF := i;
break;
}
}
if(v_EPTF_TransportIPL2_loopbackIF < 0) {
v_EPTF_TransportIPL2_loopbackIF := sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList);
v_EPTF_TransportIPL2_ethernetInterfaceList[v_EPTF_TransportIPL2_loopbackIF] := c_EPTF_TransportIPL2_EthernetInterface_init;
v_EPTF_TransportIPL2_ethernetInterfaceList[v_EPTF_TransportIPL2_loopbackIF].name := tsp_EPTF_TransportIPL2_loopbackInterface;
}
var octetstring vl_mask := f_EPTF_TransportIPL2_getIpFromHostName(tsp_EPTF_TransportIPL2_loopbackInterfaceMask);
v_EPTF_TransportIPL2_ethernetInterfaceList[v_EPTF_TransportIPL2_loopbackIF].packetFilter := {
filterString := "arp or udp or tcp and net "&
tsp_EPTF_TransportIPL2_loopbackInterfaceAddress & " mask "& tsp_EPTF_TransportIPL2_loopbackInterfaceMask,
subnets := {
{
net := f_EPTF_TransportIPL2_getIpFromHostName(tsp_EPTF_TransportIPL2_loopbackInterfaceAddress),
mask := vl_mask,
maskbits := f_EPTF_TransportIPL2_nofBitsInNetmask(vl_mask)
}
}
}
}
private function f_EPTF_TransportIPL2_openInterface(in integer pl_interfaceIdx)
runs on EPTF_TransportIPL2_CT
{
var ASP_LANL2_open_interface vl_open := {
interface_name := v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].name,
default_src_addr := omit,
promisc_mode := v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].promiscousMode,
packet_filter := f_EPTF_TransportIPL2_getPacketFilterString(pl_interfaceIdx)
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": interface open: " & log2str(vl_open));
}
IPL2_PCO.send(vl_open);
var ASP_LANL2_open_result vl_res;
var ASP_v2_LANL2_Error vl_err;
timer T_guard := 5.0;
T_guard.start;
alt {
[] IPL2_PCO.receive(ASP_LANL2_open_result:?) -> value vl_res { }
[] IPL2_PCO.receive(ASP_v2_LANL2_Error:?) -> value vl_err {
f_EPTF_TransportIPL2_error(%definitionId&": could not open interface "& v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].name);
}
[] IPL2_PCO.receive {
f_EPTF_TransportIPL2_warning(%definitionId&": unexpected message received on IPL2_PCO.");
repeat;
}
[] T_guard.timeout {
f_EPTF_TransportIPL2_error(%definitionId&": timeout while waiting for interface open result from IPL2_PCO.");
}
}
T_guard.stop;
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": interface open result: " & log2str(vl_res));
}
if(not vl_res.success) {
f_EPTF_TransportIPL2_error(%definitionId&": could not open interface "& v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].name);
}
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].interfaceId := vl_res.interface_id;
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].macAddress := vl_res.default_src_addr;
}
private function f_EPTF_TransportIPL2_closeInterface(in integer pl_interfaceIdx)
runs on EPTF_TransportIPL2_CT
{
var ASP_LANL2_close_interface vl_close := {
interface_id := v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].interfaceId
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": closing interface " & v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].name);
}
IPL2_PCO.send(vl_close);
var ASP_v2_LANL2_Error vl_err;
timer T_dummy := 0.0;
T_dummy.start;
alt {
[] IPL2_PCO.receive(ASP_v2_LANL2_Error:?) -> value vl_err {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": interface close failed: " & log2str(vl_err));
}
}
[] T_dummy.timeout { }
}
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceIdx].interfaceId := -1;
}
private function f_EPTF_TransportIPL2_openConfiguredInterfaces()
runs on EPTF_TransportIPL2_CT
{
for(var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); i:=i+1) {
if( v_EPTF_TransportIPL2_openLoopbackInterface == false and
i == v_EPTF_TransportIPL2_loopbackIF) {
f_EPTF_TransportIPL2_debug(%definitionId&": skipping open for loopback interface");
} else {
f_EPTF_TransportIPL2_genPacketFilter(i);
f_EPTF_TransportIPL2_openInterface(i);
}
}
f_EPTF_TransportIPL2_updateRouteTableToEthIF();
}
private function f_EPTF_TransportIPL2_closeConfiguredInterfaces()
runs on EPTF_TransportIPL2_CT
{
for(var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); i:=i+1) {
if(v_EPTF_TransportIPL2_ethernetInterfaceList[i].interfaceId >= 0) {
f_EPTF_TransportIPL2_closeInterface(i);
}
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_init
//
// Purpose:
// Function to initialize the IPL2 Communication port component
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - transport type
// pl_selfName - *in* - *charstring* - EPTF self name
// pl_interfaceList - *in* - <EPTF_Transport_InterfaceInformationList> interface list - for setUpInterfaces() and setDownInterfaces(), not used
// pl_enableBufferManager - *in* - *boolean* - copied from IPL4, not used
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_init(
in EPTF_Transport_TransportType pl_transportType,
in charstring pl_selfName,
in EPTF_Transport_InterfaceInformationList pl_interfaceList := {},
in boolean pl_enableBufferManager := true)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
var Result vl_result;
if (v_EPTF_TransportIPL2_initialized) {
return;
}
v_EPTF_TransportIPL2_openLoopbackInterface := tsp_EPTF_TransportIPL2_openLoopbackInterface;
if(tsp_EPTF_TransportIPL2_tcpMSS > tsp_EPTF_TransportIPL2_MTU - 40) { // 20 byte IP header, 20 byte TCP header
f_EPTF_TransportIPL2_error(%definitionId&": TCP MSS is greater than IP MTU - 40, please reconfigure.");
}
if(tsp_EPTF_TransportIPL2_tcpMSS > tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize) {
f_EPTF_TransportIPL2_warning(%definitionId&": TCP MSS is greater than the receive window size, please reconfigure.");
}
f_EPTF_TransportIPL2_initExtendedComponents(pl_selfName);
f_EPTF_HashMap_init_CT (pl_selfName);
f_EPTF_Base_registerCleanup(refers(f_EPTF_TransportIPL2_cleanup_CT));
v_EPTF_TransportIPL2_loggingMaskId :=
f_EPTF_Logging_registerComponentMasks(
tsp_EPTF_TransportIPL2_loggingComponentMask,
c_EPTF_TransportIPL2_loggingEventClasses,
EPTF_Logging_CLL);
if (tsp_EPTF_TransportIPL2_enableLogging)
{
f_EPTF_Logging_enableLocalMask(
v_EPTF_TransportIPL2_loggingMaskId,
c_EPTF_TransportIPL2_loggingClassIdx_Debug);
}
else
{
f_EPTF_Logging_disableLocalMask(
v_EPTF_TransportIPL2_loggingMaskId,
c_EPTF_TransportIPL2_loggingClassIdx_Debug);
}
v_EPTF_TransportIPL2_LGenTypesHashMapId :=
f_EPTF_str2int_HashMap_New(c_EPTF_TransportIPL2_LGenTypesHashMapName);
v_EPTF_CommPort_connectionDatabase.hashmapId :=
f_EPTF_oct2int_HashMap_New(c_EPTF_TransportIPL2_connectionDBHashMapName);
v_EPTF_TransportIPL2_nameCache.hashmapId :=
f_EPTF_str2int_HashMap_New(c_EPTF_TransportIPL2_nameCacheHashMapName);
v_EPTF_TransportIPL2_ipFragmentBufferDB.hashmapId :=
f_EPTF_oct2int_HashMap_New(c_EPTF_TransportIPL2_ipFragmentBufferDBHashMapName);
// if(tsp_EPTF_TransportIPL2_enableIpAddressFilter) {
v_EPTF_TransportIPL2_ipAddressHashmapId :=
f_EPTF_oct2int_HashMap_New(c_EPTF_TransportIPL2_ipAddressHashMapName);
// }
v_EPTF_TransportIPL2_LGenInfoFreeBusyQueue := c_EPTF_emptyFreeBusyQueue;
f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_TransportIPL2_LGenInfoFreeBusyQueue);
v_EPTF_CommPort_connectionDatabase.queue := c_EPTF_emptyFreeBusyQueue;
f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_CommPort_connectionDatabase.queue);
v_EPTF_TransportIPL2_ipFragmentBufferDB.queue := c_EPTF_emptyFreeBusyQueue;
f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_TransportIPL2_ipFragmentBufferDB.queue);
vd_EPTF_TransportIPL2_defaultReceive := activate(as_EPTF_TransportIPL2_defaultReceive());
v_EPTF_Transport_interfaceInformationList := pl_interfaceList;
f_EPTF_Transport_setUpInterfaces (IPL2, vl_result);
f_EPTF_TransportIPL2_initStatistics ();
f_EPTF_TransportIPL2_initRouting();
map(self:IPL2_PCO, system:IPL2_PCO);
if(tsp_EPTF_TransportIPL2_multipleInterfacesMode) {
f_EPTF_TransportIPL2_openConfiguredInterfaces();
}
v_EPTF_TransportIPL2_initialized := true;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_cleanup_CT
//
// Purpose:
// Function to cleanup the IPL2 Communicationport component
//
// Parameters:
// -
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_cleanup_CT() runs on EPTF_TransportIPL2_CT
{
if(not v_EPTF_TransportIPL2_initialized) { return;}
if(f_EPTF_FBQ_getLengthOfBusyChain(v_EPTF_CommPort_connectionDatabase.queue) > 0) {
var boolean vl_doWait := false;
for(var integer i:=0; i<sizeof(v_EPTF_CommPort_connectionDatabase.data); i:=i+1) {
if(f_EPTF_FBQ_itemIsBusy(i, v_EPTF_CommPort_connectionDatabase.queue)) {
if( v_EPTF_CommPort_connectionDatabase.data[i].tcpState == ESTABLISHED or
v_EPTF_CommPort_connectionDatabase.data[i].tcpState == CLOSE_WAIT) {
var Result vl_res;
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": closing socket "&
log2str(v_EPTF_CommPort_connectionDatabase.data[i]));
}
f_EPTF_Transport_close(IPL2, i, vl_res);
vl_doWait := true;
}
}
}
if(vl_doWait) {
timer T_minWait := 0.0;
timer T_maxWait := 5.0;
T_minWait.start;
T_maxWait.start;
alt {
[f_EPTF_FBQ_getLengthOfBusyChain(v_EPTF_CommPort_connectionDatabase.queue) == 0] T_minWait.timeout { }
[] T_maxWait.timeout { }
}
}
for(var integer i:=0; i<sizeof(v_EPTF_CommPort_connectionDatabase.data); i:=i+1) {
if( f_EPTF_FBQ_itemIsBusy(i, v_EPTF_CommPort_connectionDatabase.queue) and
v_EPTF_CommPort_connectionDatabase.data[i].proto == {tcp:={}} and
ispresent(v_EPTF_CommPort_connectionDatabase.data[i].remHost)) {
// TCP, not listen socket
f_EPTF_TransportIPL2_finalCloseTcp(i); // needed to cancel all scheduled event
}
}
}
f_EPTF_TransportIPL2_closeConfiguredInterfaces();
unmap(self:IPL2_PCO, system:IPL2_PCO);
if(vd_EPTF_TransportIPL2_defaultReceive != null) {
deactivate(vd_EPTF_TransportIPL2_defaultReceive);
vd_EPTF_TransportIPL2_defaultReceive := null;
}
v_EPTF_TransportIPL2_LGenInfoList := {};
f_EPTF_str2int_HashMap_Delete(c_EPTF_TransportIPL2_LGenTypesHashMapName);
f_EPTF_oct2int_HashMap_Delete(c_EPTF_TransportIPL2_connectionDBHashMapName);
f_EPTF_str2int_HashMap_Delete(c_EPTF_TransportIPL2_nameCacheHashMapName);
f_EPTF_oct2int_HashMap_Delete(c_EPTF_TransportIPL2_ipFragmentBufferDBHashMapName);
// if(tsp_EPTF_TransportIPL2_enableIpAddressFilter) {
f_EPTF_oct2int_HashMap_Delete(c_EPTF_TransportIPL2_ipAddressHashMapName);
// }
v_EPTF_TransportIPL2_LGenTypesHashMapId := -1;
v_EPTF_TransportIPL2_LGenInfoFreeBusyQueue := c_EPTF_emptyFreeBusyQueue;
v_EPTF_TransportIPL2_incStats := {-1,-1,-1,-1,-1};
v_EPTF_TransportIPL2_outStats := {-1,-1,-1,-1};
v_EPTF_TransportIPL2_incStatCounters := {0,0,0,0,0};
v_EPTF_TransportIPL2_outStatCounters := {0,0,0,0};
v_EPTF_TransportIPL2_nofErrors := -1;
v_EPTF_TransportIPL2_lastErrorString := -1;
v_EPTF_TransportIPL2_loggingMaskId := -1;
v_EPTF_CommPort_connectionDatabase := { {}, c_EPTF_emptyFreeBusyQueue, -1 }
v_EPTF_TransportIPL2_nameCache := { {}, -1 }
v_EPTF_TransportIPL2_ipPacketId := 0;
v_EPTF_TransportIPL2_ipFragmentBufferDB := { {}, c_EPTF_emptyFreeBusyQueue, -1 };
v_EPTF_TransportIPL2_ipAddressHashmapId := -1;
v_EPTF_TransportIPL2_initialized := false;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_registerMsgLenCallback4LGenType
//
// Purpose:
// Sets the message length calclutaion function for an LGen type.
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - transport type
// pl_function - *in* - <EPTF_Transport_GetMsgLen_FT> - the message length calculation function reference
// pl_msgLenArgs - *in* - <EPTF_IntegerList> - the function arguments
// pl_LGenType - *in* *charstring* - the name of the LGen type
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_registerMsgLenCallback4LGenType(
in EPTF_Transport_TransportType pl_transportType,
in EPTF_Transport_GetMsgLen_FT pl_function,
in EPTF_IntegerList pl_msgLenArgs,
in charstring pl_LGenType)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
var integer vl_fbqId;
f_EPTF_TransportIPL2_addLGenInfo(pl_LGenType, vl_fbqId);
v_EPTF_TransportIPL2_LGenInfoList[vl_fbqId].msgLenCalc :=
{
getMsgLen := pl_function,
getMsgLenArgs := pl_msgLenArgs
}
}
private function f_EPTF_TransportIPL2_checkConnId(in charstring pl_caller, in integer pl_connId)
runs on EPTF_TransportIPL2_CT
{
if(c_EPTF_Common_debugSwitch) {
f_EPTF_Base_assert(pl_caller&": Invalid connection ID " & int2str(pl_connId),
pl_connId >= 0 and
pl_connId < sizeof(v_EPTF_CommPort_connectionDatabase.data) and
f_EPTF_FBQ_itemIsBusy(pl_connId, v_EPTF_CommPort_connectionDatabase.queue));
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_setUserData
//
// Purpose:
// Function to set user data
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - transport type
// pl_connId - *in* - <ConnectionId> - The ID of the connection
// pl_userData - *in* - *integer* - The data information
// pl result - *out* - <Result> - result
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_setUserData(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
in integer pl_userData,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
v_EPTF_CommPort_connectionDatabase.data[pl_connId].userData := pl_userData;
pl_result := c_emptyResult;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_getUserData
//
// Purpose:
// Function to get user data
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - transport type
// pl_connId - *in* - <ConnectionId> - The ID of the connection
// pl_userData - *out* - <integer> - The data information
// pl_result - *out* - <Result> - result
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_getUserData(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
out integer pl_userData,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
pl_userData := v_EPTF_CommPort_connectionDatabase.data[pl_connId].userData;
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_setMsgHandler
//
// Purpose:
// Function to set callback function for handling incoming messages
//
// Parameters:
// pl_msghandler - *in* - <EPTF_Transport_MsgCallback_FT> - The handler function to the receive template
// pl_LGenType - *in* - *charstring* - the type of the LGen component registered this function
//
// Return Value:
// -
//
// Errors:
// pl_LgenType should not be empty string.
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_setMsgHandler (
in EPTF_Transport_MsgCallback_FT pl_msghandler,
in charstring pl_LGenType)
runs on EPTF_TransportIPL2_CT
{
if (pl_LGenType=="")
{
f_EPTF_TransportIPL2_error(%definitionId&": LGenType must be specified.");
}
else
{
var integer vl_fbqId;
f_EPTF_TransportIPL2_addLGenInfo(pl_LGenType, vl_fbqId);
v_EPTF_TransportIPL2_LGenInfoList[vl_fbqId].msgHandler := pl_msghandler;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_addLGenInfo
//
// Purpose:
// Retreives the index of the database entry belonging to an LGen type.
// If the entiry does not exist, adds a new entry to the database.
//
// Parameters:
// pl_LGenType - *in* *charstring* - the LGen type name
// pl_idx - *inout* *integer* - the returned index of the database entry
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_addLGenInfo(in charstring pl_LGenType, inout integer pl_idx)
runs on EPTF_TransportIPL2_CT
{
if (f_EPTF_str2int_HashMap_Find(v_EPTF_TransportIPL2_LGenTypesHashMapId, pl_LGenType, pl_idx))
{
return;
}
pl_idx := f_EPTF_FBQ_getOrCreateFreeSlot(v_EPTF_TransportIPL2_LGenInfoFreeBusyQueue);
f_EPTF_FBQ_moveFromFreeToBusyTail(pl_idx,v_EPTF_TransportIPL2_LGenInfoFreeBusyQueue);
f_EPTF_str2int_HashMap_Insert(v_EPTF_TransportIPL2_LGenTypesHashMapId, pl_LGenType, pl_idx);
v_EPTF_TransportIPL2_LGenInfoList[pl_idx] := c_EPTF_TransportIPL2_LGenInfo_init
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_send
//
// Purpose:
// Function to send a message on a connection
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_connId - *in* - <ConnectionId> - connection ID
// pl_msg - *in* - *octetstring* - the message to send
// pl_result - *out* - <Result> - result
// pl_needBuffering - *in* - *boolean*
//
// Return Value:
// *boolean* - true on success, false on error
//
// Errors:
// - invalid connection ID
// - no connection (remote peer) exists for socket
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_send(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
in octetstring pl_msg,
out Result pl_result,
in boolean pl_needBuffering := false,
in ProtoTuple pl_proto:={unspecified:={}})
runs on EPTF_TransportIPL2_CT
return boolean
{
var integer vl_i := 0;
var octetstring vl_appendMsg := ''O;
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
if (ispresent(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost)){
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto == {udp:={}}) {
f_EPTF_TransportIPL2_sendUdpOverL2(
v_EPTF_CommPort_connectionDatabase.data[pl_connId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_connId].locPort,
v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost,
v_EPTF_CommPort_connectionDatabase.data[pl_connId].remPort,
pl_msg, pl_connId);
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto == {tcp:={}}) {
if(not f_EPTF_TransportIPL2_tcpSendMessage(pl_connId, pl_msg, pl_result)) { return false; }
} else {
f_EPTF_TransportIPL2_error(%definitionId&": unsupported protocol "&
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto));
}
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outMessage);
return true;
} else {
pl_result.errorCode := ERROR_INVALID_CONNECTION
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto == {tcp:={}}) {
pl_result.os_error_code := c_EPTF_TransportIPL2_errorCantSendOnListenSocket;
pl_result.os_error_text := "cannot send on TCP listen socket";
} else {
pl_result.os_error_code := c_EPTF_TransportIPL2_errorSocketIsNotConnected;
pl_result.os_error_text := "socket is not connected";
}
f_EPTF_TransportIPL2_warning(%definitionId&": "&pl_result.os_error_text);
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_lastErrorString, {charstringVal:=%definitionId&": "&pl_result.os_error_text});
f_EPTF_TransportIPL2_increaseErrors();
pl_result.errorCode := ERROR_INVALID_CONNECTION
return false;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_sendTo
//
// Purpose:
// Function to send a message to a remote peer
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_connId - *in* - <ConnectionId> - connection ID
// pl_remHost - *in* - <HostName> - remote host
// pl_remotePort - *in* - <PortNumber> - remote port number
// pl_msg - *in* - *octetstring* - the message to send
// pl_result - *out* - <Result> - result
// pl_needBuffering - *in* - <boolean> - is Buffering needed?
// Return Value:
// *boolean* - true on success, false on error
//
// Errors:
// - invalid connection ID
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_sendTo(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
in HostName pl_remoteHost,
in PortNumber pl_remotePort,
in octetstring pl_msg,
out Result pl_result,
in boolean pl_needBuffering := false)
runs on EPTF_TransportIPL2_CT
return boolean
{
var integer vl_i := 0;
var octetstring vl_appendMsg := ''O;
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
var EPTF_TransportIPL2_IpAddress vl_ipAddr := f_EPTF_TransportIPL2_getIpFromHostName(pl_remoteHost);
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto == {udp:={}}) {
f_EPTF_TransportIPL2_sendUdpOverL2(
v_EPTF_CommPort_connectionDatabase.data[pl_connId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_connId].locPort,
vl_ipAddr,
pl_remotePort,
pl_msg, pl_connId);
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto == {tcp:={}}) {
if(not ispresent(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost)) {
f_EPTF_TransportIPL2_warning(%definitionId&": cannot send on TCP listen socket.");
pl_result.errorCode := ERROR_INVALID_INPUT_PARAMETER
pl_result.os_error_code := c_EPTF_TransportIPL2_errorCantSendOnListenSocket;
pl_result.os_error_text := "cannot send on TCP listen socket";
return false;
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
//debug-check the remote IP address and port number
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost != vl_ipAddr) {
f_EPTF_TransportIPL2_debug(%definitionId&": the specified remote host "&log2str(vl_ipAddr)&
" doesn't match the connection's remote address "&
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost));
}
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remPort != pl_remotePort) {
f_EPTF_TransportIPL2_debug(%definitionId&": the specified remote port "&int2str(pl_remotePort)&
" doesn't match the connection's remote port "&
int2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remPort));
}
}
return f_EPTF_Transport_send(pl_transportType, pl_connId, pl_msg, pl_result, pl_needBuffering);
} else {
f_EPTF_TransportIPL2_error(%definitionId&": unsupported protocol "&
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto));
}
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outMessage);
return true;
}
group EPTF_TransportIPL2_ConnectionHandling
{
private function f_EPTF_TransportIPL2_getIpFromHostName(in charstring pl_hostName)
runs on EPTF_TransportIPL2_CT
return EPTF_TransportIPL2_IpAddress
{
var integer vl_idx := -1;
if(f_EPTF_str2int_HashMap_Find(v_EPTF_TransportIPL2_nameCache.hashmapId, pl_hostName, vl_idx)) {
return v_EPTF_TransportIPL2_nameCache.addresses[vl_idx];
} else {
vl_idx := sizeof(v_EPTF_TransportIPL2_nameCache.addresses);
f_EPTF_str2int_HashMap_Insert(v_EPTF_TransportIPL2_nameCache.hashmapId, pl_hostName, vl_idx);
v_EPTF_TransportIPL2_nameCache.addresses[vl_idx] := f_OctetIpv4(pl_hostName);
if(lengthof(v_EPTF_TransportIPL2_nameCache.addresses[vl_idx]) == 0) {
v_EPTF_TransportIPL2_nameCache.addresses[vl_idx] := f_OctetIpv4(f_getIpAddr(pl_hostName));
}
return v_EPTF_TransportIPL2_nameCache.addresses[vl_idx];
}
}
// FIXME: should be in TCC Useful Functions (TCCConversion_Functions)
/* function f_EPTF_TransportIPL2_ip2str(in EPTF_TransportIPL2_IpAddress pl_ipAddr)
return charstring
{
return int2str(oct2int(pl_ipAddr[0])) & "." &
int2str(oct2int(pl_ipAddr[1])) & "." &
int2str(oct2int(pl_ipAddr[2])) & "." &
int2str(oct2int(pl_ipAddr[3]));
}*/
external function f_EPTF_TransportIPL2_ip2str(in octetstring pl_ipAddr)
return charstring;
friend function f_EPTF_Transport_registerConnCallbacks(
in EPTF_Transport_TransportType pl_transportType,
in charstring pl_LGenType,
in EPTF_Transport_ConnectionOpened_FT pl_connOpenedFn,
in EPTF_Transport_ConnectionClosed_FT pl_connClosedFn)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
if (pl_LGenType=="") {
f_EPTF_Base_assert(%definitionId&": LGenType must be specified.", pl_LGenType!="");
}
var integer vl_fbqId;
f_EPTF_TransportIPL2_addLGenInfo(pl_LGenType, vl_fbqId);
v_EPTF_TransportIPL2_LGenInfoList[vl_fbqId].connOpenedFn := pl_connOpenedFn;
v_EPTF_TransportIPL2_LGenInfoList[vl_fbqId].connClosedFn := pl_connClosedFn;
}
friend function f_EPTF_Transport_setConnectionUniqueId(
in ConnectionId pl_connId,
in integer pl_uniqueId)
runs on EPTF_TransportIPL2_CT
{
v_EPTF_CommPort_connectionDatabase.data[pl_connId].uniqueId := pl_uniqueId;
}
friend function f_EPTF_Transport_getConnectionUniqueId(
in ConnectionId pl_connId)
runs on EPTF_TransportIPL2_CT
return integer
{
return v_EPTF_CommPort_connectionDatabase.data[pl_connId].uniqueId;
}
private function f_EPTF_TransportIPL2_newLocalPort(
in ProtoTuple pl_proto,
in octetstring pl_hostAddr,
in integer pl_portNumber,
in integer pl_lgenIdx,
in octetstring pl_remoteHostAddr := ''O, // optional, should be empty in case of UDP or TCP listen socket
in integer pl_remotePortNumber := -1) // optional, should be -1 in case of UDP or TCP listen socket
runs on EPTF_TransportIPL2_CT
return integer
{
if(c_EPTF_Common_debugSwitch) {
f_EPTF_Base_assert(%definitionId&": invalid port number "&int2str(pl_portNumber),
pl_portNumber > 0 and // not >=0 because 0 would mean automatic allocation of a local port number which is not supported
pl_portNumber <= 65535);
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&"(\""&
f_EPTF_TransportIPL2_ip2str(pl_hostAddr)&"\", "&int2str(pl_portNumber)&", \""&
f_EPTF_TransportIPL2_ip2str(pl_remoteHostAddr)&"\", "&int2str(pl_remotePortNumber)&")");
}
// if(tsp_EPTF_TransportIPL2_enableIpAddressFilter) {
f_EPTF_TransportIPL2_addIp(pl_hostAddr);
// }
var integer vl_idx := f_EPTF_TransportIPL2_getConnectionIdx(pl_proto, pl_hostAddr, pl_portNumber, pl_remoteHostAddr, pl_remotePortNumber);
if (vl_idx == -1) {
// no index found, add new element
vl_idx := f_EPTF_FBQ_getOrCreateFreeSlot(v_EPTF_CommPort_connectionDatabase.queue);
f_EPTF_FBQ_moveFromFreeToBusyTail(vl_idx, v_EPTF_CommPort_connectionDatabase.queue);
v_EPTF_CommPort_connectionDatabase.data[vl_idx] := c_EPTF_TransportIPL2_Connection_init;
v_EPTF_CommPort_connectionDatabase.data[vl_idx].uniqueId := vl_idx;
v_EPTF_CommPort_connectionDatabase.data[vl_idx].lgenIdx := pl_lgenIdx;
if(pl_remoteHostAddr == ''O) {
v_EPTF_CommPort_connectionDatabase.data[vl_idx].remHost := omit;
} else {
v_EPTF_CommPort_connectionDatabase.data[vl_idx].remHost := pl_remoteHostAddr;
}
v_EPTF_CommPort_connectionDatabase.data[vl_idx].remPort := pl_remotePortNumber;
v_EPTF_CommPort_connectionDatabase.data[vl_idx].locHost := pl_hostAddr;
v_EPTF_CommPort_connectionDatabase.data[vl_idx].locPort := pl_portNumber;
v_EPTF_CommPort_connectionDatabase.data[vl_idx].proto := pl_proto;
if(pl_proto == {udp:={}}){
v_EPTF_CommPort_connectionDatabase.data[vl_idx].hashKey := f_EPTF_TransportIPL2_getHashKey4UDP(pl_hostAddr, pl_portNumber);
} else if(pl_proto == {tcp:={}}) {
v_EPTF_CommPort_connectionDatabase.data[vl_idx].hashKey :=
f_EPTF_TransportIPL2_getHashKey4TCP(pl_hostAddr, pl_portNumber, pl_remoteHostAddr, pl_remotePortNumber);
if(pl_remoteHostAddr != ''O) {
v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData := c_EPTF_TransportIPL2_TcpConnectionData_init;
v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.sendFifo_bufferID := f_EPTF_Buffer_new();
v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.mss := tsp_EPTF_TransportIPL2_tcpMSS; // updated when hadnling incoming SYN segment
v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.sendSeqNum := f_EPTF_TransportIPL2_tcpIsn();
v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.sendWindowStart := v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.sendSeqNum;
f_EPTF_FBQ_initFreeBusyQueue(v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.sendBuffer.queue);
f_EPTF_SegmentBuffer_initBuffer(v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.segmentBuffer);
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": send window start initialized to "&
int2str(v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.sendWindowStart));
}
v_EPTF_CommPort_connectionDatabase.data[vl_idx].tcpConnectionData.smoothedRTT := tsp_EPTF_TransportIPL2_tcpMinRetransmitTime
//v_EPTF_CommPort_connectionDatabase.data[vl_idx].receiveWindowSize := tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize;
}
} else {
f_EPTF_Base_assert(%definitionId&": Unsupported protocol.", false);
}
f_EPTF_oct2int_HashMap_Insert(v_EPTF_CommPort_connectionDatabase.hashmapId, v_EPTF_CommPort_connectionDatabase.data[vl_idx].hashKey, vl_idx);
if(v_EPTF_TransportIPL2_LGenInfoList[pl_lgenIdx].connOpenedFn != null) {
v_EPTF_TransportIPL2_LGenInfoList[pl_lgenIdx].connOpenedFn.apply(IPL2, vl_idx);
}
return v_EPTF_CommPort_connectionDatabase.data[vl_idx].uniqueId;
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": already listening on " & f_EPTF_TransportIPL2_ip2str(pl_hostAddr) & ":" & int2str(pl_portNumber));
return v_EPTF_CommPort_connectionDatabase.data[vl_idx].uniqueId;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_tcpIsn
//
// Purpose:
// Function to get an Initial Sequence Number
//
// Parameters:
// -
//
// Return Value:
// *integer* - Initial Sequence Number
//
// Errors:
// -
///////////////////////////////////////////////////////////
external function f_EPTF_TransportIPL2_tcpIsn() return integer;
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_listen
//
// Purpose:
// Function to listen on a socket
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_proto - *in* - <ProtoTuple> - protocol
// pl_hostName - *in* - <HostName> - local host
// pl_portNumber - *in* - <PortNumber> - local port number
// pl_LGenType - *in* - *charstring* - LGen type
// pl_result - *out* - <Result> - result
// pl_automaticBuffering - *out* - *boolean* - automatic buffering
// Return Value:
// *integer* - connection/socket ID, or -1 on error
//
// Errors:
// - invalid LGenType
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_listen(
in EPTF_Transport_TransportType pl_transportType,
in ProtoTuple pl_proto,
in HostName pl_hostName,
in PortNumber pl_portNumber,
in charstring pl_LGenType,
out Result pl_result,
in boolean pl_automaticBuffering := false,
in EPTF_TransportIPL2_VLAN_TCIs pl_vlanTCIs := {})
runs on EPTF_TransportIPL2_CT
return integer
{
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
if(not ischosen(pl_proto.udp) and not ischosen(pl_proto.tcp)) {
pl_result.errorCode := ERROR_UNSUPPORTED_PROTOCOL;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorUnsupportedPorotocol;
pl_result.os_error_text := "unsupported protocol";
return -1;
}
var integer vl_lgenIdx
if (f_EPTF_str2int_HashMap_Find(v_EPTF_TransportIPL2_LGenTypesHashMapId,pl_LGenType, vl_lgenIdx))
{
var integer vl_idx := f_EPTF_TransportIPL2_newLocalPort(pl_proto, f_EPTF_TransportIPL2_getIpFromHostName(pl_hostName), pl_portNumber, vl_lgenIdx);
v_EPTF_CommPort_connectionDatabase.data[vl_idx].vlanTCIs := pl_vlanTCIs;
if(pl_proto == {tcp:={}}){
f_EPTF_TransportIPL2_setSocketState(vl_idx, LISTEN);
}
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outListen);
return vl_idx;
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": LGenType "&pl_LGenType&" not found in the hashmap");
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_lastErrorString, {charstringVal:="LGenType "&pl_LGenType&" not found in the hashmap"});
f_EPTF_TransportIPL2_increaseErrors();
pl_result.errorCode := ERROR_INVALID_INPUT_PARAMETER;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorInvalidLgenType;
pl_result.os_error_text := "invalid LGenType "&pl_LGenType;
return -1;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_setOpt
//
// Purpose:
// Function to set the options on a socket
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_connId - *in* - <Socket_API_Definitions.ConnectionId> - connection id
// pl_proto - *in* <ProtoTuple> - protocol
// pl_result - *out* - <Result> - result
// // pl_options - *in* - <OptionList> - List of Options to be set
//
// Return Value:
// *integer* - 0 if OK, or -1 on error
//
// Errors:
// - invalid LGenType
///////////////////////////////////////////////////////////
friend /* This should be public after optionList is supported (add this also to the public function list in the module descr.) */ function f_EPTF_Transport_setOpt(
in EPTF_Transport_TransportType pl_transportType,
in integer pl_connId,
in ProtoTuple pl_proto:={unspecified:={}},
out Result pl_result//,
// in OptionList pl_options
)
runs on EPTF_TransportIPL2_CT
return integer
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
pl_result.errorCode := ERROR_UNSUPPORTED_PROTOCOL;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorUnsupportedPorotocol;
pl_result.os_error_text := "unsupported protocol";
return -1;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_connect
//
// Purpose:
// Function to create a new connection
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_proto - *in* - <ProtoTuple> - protocol
// pl_localHost - *in* <HostName> - local host
// pl_localPort - *in* <PortNumber> - local port number
// pl_remoteHost - *in* <HostName> - remote host
// pl_remotePort - *in* <PortNumber> - remote port number
// pl_LGenType - *in* - *charstring* - LGen type
// pl_result - *out* - <Result> - result
// pl_automaticBuffering - *out* - *boolean* - automatic buffering
// Return Value:
// *integer* - connection/socket ID, or -1 on error
//
// Errors:
// - invalid LGenType
// - already listening on localHost:localPort
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_connect(
in EPTF_Transport_TransportType pl_transportType,
in ProtoTuple pl_proto,
in HostName pl_localHost,
in PortNumber pl_localPort,
in HostName pl_remoteHost,
in PortNumber pl_remotePort,
in charstring pl_LGenType,
out Result pl_result,
in boolean pl_automaticBuffering := false,
in EPTF_TransportIPL2_VLAN_TCIs pl_vlanTCIs := {})
runs on EPTF_TransportIPL2_CT
return integer
{
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
if(not ischosen(pl_proto.udp) and not ischosen(pl_proto.tcp)) {
pl_result.errorCode := ERROR_UNSUPPORTED_PROTOCOL;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorUnsupportedPorotocol;
pl_result.os_error_text := "unsupported protocol";
return -1;
}
var integer vl_lgenIdx
if (f_EPTF_str2int_HashMap_Find(v_EPTF_TransportIPL2_LGenTypesHashMapId,pl_LGenType, vl_lgenIdx))
{
var integer vl_idx := f_EPTF_TransportIPL2_newLocalPort(
pl_proto,
f_EPTF_TransportIPL2_getIpFromHostName(pl_localHost),
pl_localPort,
vl_lgenIdx,
f_EPTF_TransportIPL2_getIpFromHostName(pl_remoteHost),
pl_remotePort);
v_EPTF_CommPort_connectionDatabase.data[vl_idx].vlanTCIs := pl_vlanTCIs;
if(pl_proto == {tcp:={}}){
// initiate an outgoing connection
// 1st step of 3-way handshake-> send SYN
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(
vl_idx,
c_EPTF_TransportIPL2_TCPControlBit_syn,
''O);
f_EPTF_TransportIPL2_setSocketState(vl_idx, SYN_SENT);
}
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outConnect);
pl_result.connId := vl_idx;
return vl_idx;
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": LGenType "&pl_LGenType&" not found in the hashmap");
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_lastErrorString, {charstringVal:="LGenType "&pl_LGenType&" not found in the hashmap"});
f_EPTF_TransportIPL2_increaseErrors();
pl_result.errorCode := ERROR_INVALID_INPUT_PARAMETER;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorInvalidLgenType;
pl_result.os_error_text := "invalid LGenType "&pl_LGenType;
return -1;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_connectById
//
// Purpose:
// Function to connect an existing UDP socket to a remote peer
//
// Parameters:
// pl_connId - *in* *integer* - local connection/socket ID
// pl_remoteHost - *in* *charstring* - remote host
// pl_remotePort - *in* *integer* - remote port number
//
// Return Value:
// -
//
// Errors:
// - invalid connection/socket ID
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_connectById(
in integer pl_connId,
in charstring pl_remoteHost,
in integer pl_remotePort)
runs on EPTF_TransportIPL2_CT
{
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto != {udp:={}}) {
f_EPTF_TransportIPL2_error(%definitionId&": called for socket with protocol " &
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto));
}
v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost := f_EPTF_TransportIPL2_getIpFromHostName(pl_remoteHost);
v_EPTF_CommPort_connectionDatabase.data[pl_connId].remPort := pl_remotePort;
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outConnect);
}
private function f_EPTF_TransportIPL2_finalCloseTcp(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": final-closing socket " & int2str(pl_socketId));
}
f_EPTF_TransportIPL2_cancelSendTcpAck(pl_socketId);
f_EPTF_TransportIPL2_cancelTimeWait(pl_socketId);
for(var integer i:=0; i<sizeof(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments); i:=i+1) {
if(f_EPTF_FBQ_itemIsBusy(i, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.queue)) {
f_EPTF_TransportIPL2_cancelTcpRetransmission(pl_socketId, i);
}
}
if(f_EPTF_SegmentBuffer_getContinousLen(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.segmentBuffer) != 0) {
f_EPTF_TransportIPL2_warning(%definitionId&": discarding "&
int2str(f_EPTF_SegmentBuffer_getContinousLen(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.segmentBuffer)) &
" bytes from the receive buffer.");
}
f_EPTF_TransportIPL2_setSocketState(pl_socketId, CLOSED);
if(v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_socketId].lgenIdx].connClosedFn != null) {
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_socketId].lgenIdx].connClosedFn.apply(IPL2, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].uniqueId);
}
f_EPTF_FBQ_moveFromBusyToFreeTail(pl_socketId, v_EPTF_CommPort_connectionDatabase.queue);
f_EPTF_oct2int_HashMap_Erase(v_EPTF_CommPort_connectionDatabase.hashmapId, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].hashKey);
v_EPTF_CommPort_connectionDatabase.data[pl_socketId] := c_EPTF_TransportIPL2_Connection_init;
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outClose);
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_close
//
// Purpose:
// Function to close a connection or listening socket
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_connId - *in* <ConnectionId> - connection/socket ID
// pl_result - *out* - <Result> - result
// Return Value:
// *boolean* - true on success
//
// Errors:
// - invalid connection/socket ID
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_close(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
out Result pl_result,
in ProtoTuple pl_proto:={unspecified:={}})
runs on EPTF_TransportIPL2_CT
return boolean
{
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
// f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
if(pl_connId >= 0 and pl_connId < sizeof(v_EPTF_CommPort_connectionDatabase.data) and
f_EPTF_FBQ_itemIsBusy(pl_connId, v_EPTF_CommPort_connectionDatabase.queue)) {
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto == {tcp:={}} and
ispresent(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost)) {
// TCP, not listen socket
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
if(f_EPTF_Buffer_get_read_len(v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpConnectionData.sendFifo_bufferID) != 0)
{
// don't close the connection until there's anything in the send FIFO
// in this case, delay the close until the last segment sent from the FIFO
v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpConnectionData.pendingClose := true;
f_EPTF_TransportIPL2_debug(%definitionId&": there's data in the send FIFO, delaying close");
return true;
}
} else {
if(lengthof(v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpConnectionData.sendFifo) != 0 )
{
// don't close the connection until there's anything in the send FIFO
// in this case, delay the close until the last segment sent from the FIFO
v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpConnectionData.pendingClose := true;
f_EPTF_TransportIPL2_debug(%definitionId&": there's data in the send FIFO, delaying close");
return true;
}
}
if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpState == ESTABLISHED or
v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpState == SYN_RECEIVED) { // active close
f_EPTF_TransportIPL2_setSocketState(pl_connId, FIN_WAIT1);
f_EPTF_TransportIPL2_sendTcpFin(pl_connId);
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpState == CLOSE_WAIT) { // passive close
f_EPTF_TransportIPL2_setSocketState(pl_connId, LAST_ACK);
f_EPTF_TransportIPL2_sendTcpFin(pl_connId);
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpState == SYN_SENT) {
f_EPTF_TransportIPL2_finalCloseTcp(pl_connId);
// don't send FIN
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": called in invalid state " &
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].tcpState));
//f_EPTF_TransportIPL2_sendTcpFin(pl_connId);
f_EPTF_TransportIPL2_finalCloseTcp(pl_connId);
}
} else {
// UDP or TCP listen socket
if(v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connId].lgenIdx].connClosedFn != null) {
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connId].lgenIdx].connClosedFn.apply(IPL2, v_EPTF_CommPort_connectionDatabase.data[pl_connId].uniqueId);
}
f_EPTF_FBQ_moveFromBusyToFreeTail(pl_connId, v_EPTF_CommPort_connectionDatabase.queue);
f_EPTF_oct2int_HashMap_Erase(v_EPTF_CommPort_connectionDatabase.hashmapId, v_EPTF_CommPort_connectionDatabase.data[pl_connId].hashKey);
v_EPTF_CommPort_connectionDatabase.data[pl_connId] := c_EPTF_TransportIPL2_Connection_init;
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_outClose);
}
return true;
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": invalid connection ID " & int2str(pl_connId) &
". Size of connection DB: " & int2str(sizeof(v_EPTF_CommPort_connectionDatabase.data)));
pl_result.errorCode := ERROR_INVALID_INPUT_PARAMETER
pl_result.os_error_code := c_EPTF_TransportIPL2_errorInvalidConnectionId;
pl_result.os_error_text := "invalid connection ID "&int2str(pl_connId);
return false;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_getLocalHost
//
// Purpose:
// Function to get the local host of a socket
//
// Parameters:
// pl_connId - *in* *integer* - connection/socket ID
//
// Return Value:
// *octetstring* - local host IP address
//
// Errors:
// - invalid connection/socket ID
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_getLocalHost(in integer pl_connId)
runs on EPTF_TransportIPL2_CT
return octetstring
{
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
return v_EPTF_CommPort_connectionDatabase.data[pl_connId].locHost;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_getLocalPort
//
// Purpose:
// Function to get the local port number of a socket
//
// Parameters:
// pl_connId - *in* *integer* - connection/socket ID
//
// Return Value:
// *integer* - local port number
//
// Errors:
// - invalid connection/socket ID
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_getLocalPort(in integer pl_connId)
runs on EPTF_TransportIPL2_CT
return integer
{
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
return v_EPTF_CommPort_connectionDatabase.data[pl_connId].locPort;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_getProto
//
// Purpose:
// Function to get the protocol of a socket
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - transport type
// pl_connId - *in* <ConnectionId> - connection/socket ID
// pl_proto - *out* - <ProtoTuple> - protocol
// pl_result - *out* - <Result> - result
//
// Return Value:
// -
//
// Errors:
// - invalid connection/socket ID
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_getProto(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
out ProtoTuple pl_proto,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
pl_proto := v_EPTF_CommPort_connectionDatabase.data[pl_connId].proto;
}
external function f_EPTF_TransportIPL2_getHashKey4UDP(
in octetstring pl_locAddr,
in integer pl_locPort)
return octetstring;
external function f_EPTF_TransportIPL2_getHashKey4TCP(
in octetstring pl_locAddr,
in integer pl_locPort,
in octetstring pl_remAddr,
in integer pl_remPort)
return octetstring;
private function f_EPTF_TransportIPL2_getConnectionIdx(
in ProtoTuple pl_proto,
in octetstring pl_locAddr,
in integer pl_locPort,
in octetstring pl_remAddr := ''O,
in integer pl_remPort := -1)
runs on EPTF_TransportIPL2_CT
return integer
{
var integer vl_idx := -1;
if(pl_proto == {udp:={}}) {
if(f_EPTF_oct2int_HashMap_Find(
v_EPTF_CommPort_connectionDatabase.hashmapId,
f_EPTF_TransportIPL2_getHashKey4UDP(pl_locAddr, pl_locPort),
vl_idx)) {
return vl_idx;
}
} else if(pl_proto == {tcp:={}}) {
if(f_EPTF_oct2int_HashMap_Find(
v_EPTF_CommPort_connectionDatabase.hashmapId,
f_EPTF_TransportIPL2_getHashKey4TCP(
pl_locAddr,
pl_locPort,
pl_remAddr,
pl_remPort),
vl_idx)) {
return vl_idx;
}
}
return -1;
}
private function f_EPTF_TransportIPL2_sendUdpOverL2 (
in EPTF_TransportIPL2_IpAddress pl_locHost,
in integer pl_locPort,
in EPTF_TransportIPL2_IpAddress pl_remHost,
in integer pl_remPort,
in octetstring pl_msg,
in ConnectionId pl_connId := -1)
runs on EPTF_TransportIPL2_CT
{
if ( lengthof(pl_msg) > 65535 - 8 ) {
f_EPTF_TransportIPL2_warning("UDP packet length is greater than (65535 - 8).");
f_EPTF_Var_adjustContent(v_EPTF_TransportIPL2_lastErrorString, {charstringVal:="UDP packet length is greater than (65535 - 8)."});
f_EPTF_TransportIPL2_increaseErrors();
return;
}
var octetstring ip_payload := f_EPTF_TransportIPL2_encodeUDPWithChecksum(pl_locHost, pl_locPort, pl_remHost, pl_remPort, pl_msg);
var integer vl_msgLength := lengthof(ip_payload);
var integer vl_maxLength := tsp_EPTF_TransportIPL2_MTU - 20;
if(vl_msgLength < vl_maxLength) {
f_EPTF_TransportIPL2_sendIpOverL2(pl_locHost, pl_remHost, c_ip_proto_udp, ip_payload, false, 0, pl_connId);
} else {
var integer vl_fragmentOffsetInc := vl_maxLength / c_EPTF_TransportIPL2_ipFragmentOffsetScale;
vl_maxLength := vl_fragmentOffsetInc * c_EPTF_TransportIPL2_ipFragmentOffsetScale;
var integer vl_fragmentOffset := 0;
while ( vl_msgLength > 0 ){
if (vl_msgLength < vl_maxLength){
f_EPTF_TransportIPL2_sendIpOverL2(pl_locHost, pl_remHost, c_ip_proto_udp, ip_payload, false, vl_fragmentOffset, pl_connId);
break;
} else {
f_EPTF_TransportIPL2_sendIpOverL2(pl_locHost, pl_remHost, c_ip_proto_udp, substr(ip_payload, 0, vl_maxLength ), true, vl_fragmentOffset, pl_connId);
ip_payload := substr(ip_payload, vl_maxLength, lengthof(ip_payload) - vl_maxLength);
vl_msgLength := vl_msgLength - vl_maxLength;
vl_fragmentOffset := vl_fragmentOffset + vl_fragmentOffsetInc;
}
}
}
v_EPTF_TransportIPL2_ipPacketId := v_EPTF_TransportIPL2_ipPacketId + 1;
if(v_EPTF_TransportIPL2_ipPacketId > 65535) { v_EPTF_TransportIPL2_ipPacketId := 0; }
}
// fragments pl_message into segments, calls f_EPTF_TransportIPL2_sendTcpSegmentOverL2 for each segment, sets PSH flag for last segment
private function f_EPTF_TransportIPL2_tcpSendMessage (
in integer pl_sockId,
in octetstring pl_message,
inout Result pl_result)
runs on EPTF_TransportIPL2_CT
return boolean
{
if(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState != ESTABLISHED and
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState != CLOSE_WAIT) {
if(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState == SYN_SENT or
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState == SYN_RECEIVED) {
/* pl_result.errorCode := ERROR_TEMPORARILY_UNAVAILABLE;
pl_result.os_error_code := omit;
pl_result.os_error_text := omit;
return false;*/
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": cannot send in state "&
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState)&
", appending message to fifo.");
}
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
f_EPTF_Buffer_put_os(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID, pl_message);
} else {
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo :=
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo &
pl_message; // store the rest of the message to be sent after the ACK of the SYN or SYN+ACK
}
return true;
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": called in state "&
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState));
pl_result.errorCode := ERROR_INVALID_CONNECTION;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorInvalidSocketState;
pl_result.os_error_text := "cannot send on socket with state "&log2str(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpState);
return false;
}
}
if(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.pendingClose) {
f_EPTF_TransportIPL2_warning(%definitionId&": trying to send on a socket which was closed");
pl_result.errorCode := ERROR_INVALID_CONNECTION;
pl_result.os_error_code := c_EPTF_TransportIPL2_errorSocketClosed;
pl_result.os_error_text := "trying to send on a socket which was closed";
return false;
}
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
if (f_EPTF_Buffer_get_read_len(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID) > 0){
f_EPTF_TransportIPL2_debug(%definitionId&": appending the whole message to fifo.");
f_EPTF_Buffer_put_os(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID, pl_message);
return true;
}
} else {
if(lengthof(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo) > 0){
f_EPTF_TransportIPL2_debug(%definitionId&": appending the whole message to fifo.");
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo :=
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo & pl_message;
return true;
}
}
var integer vl_msgLength := lengthof(pl_message);
var integer vl_msgOffset := 0;
var integer vl_mss := v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.mss;
var integer vl_flags := c_EPTF_TransportIPL2_TCPControlBit_ack;
do {
if(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.remoteWindowSize < vl_mss) {
if(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.remoteWindowSize > 0) {
vl_mss := v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.remoteWindowSize; // fit the segment in the window
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": remote window full, appending the remaining "&
int2str(lengthof(pl_message)-vl_msgOffset)&" bytes of the message to fifo.");
}
// store the rest of the message to be sent later, when the window becomes non-zero (hopefully the next incoming ACK)
if(vl_msgOffset > 0) {
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
f_EPTF_Buffer_clear(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID);
f_EPTF_Buffer_put_os(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID, substr(pl_message, vl_msgOffset, lengthof(pl_message) - vl_msgOffset));
} else {
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo :=
substr(pl_message, vl_msgOffset, lengthof(pl_message) - vl_msgOffset);
}
} else {
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
f_EPTF_Buffer_clear(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID);
f_EPTF_Buffer_put_os(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo_bufferID, pl_message);
} else {
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendFifo := pl_message;
}
}
break;
}
}
if (vl_msgLength <= vl_mss){
vl_flags := vl_flags + c_EPTF_TransportIPL2_TCPControlBit_psh;
if(vl_msgOffset > 0) {
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(pl_sockId, vl_flags, substr(pl_message, vl_msgOffset, vl_msgLength));
} else {
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(pl_sockId, vl_flags, pl_message);
}
break;
} else {
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(pl_sockId, vl_flags, substr(pl_message, vl_msgOffset, vl_mss ));
vl_msgLength := vl_msgLength - vl_mss;
vl_msgOffset := vl_msgOffset + vl_mss;
}
} while(true);
return true;
}
private function f_EPTF_TransportIPL2_retransmitTcpSegment(
in EPTF_ScheduledAction pl_action, // pl_action.actionId[0]: socket ID, pl_action.actionId[1]: send-segment buffer
in integer pl_eventIndex)
runs on EPTF_TransportIPL2_CT
return boolean
{
f_EPTF_SchedulerComp_refreshSnapshotTime();
if(f_EPTF_SchedulerComp_snapshotTime() >
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.sendBuffer.segments[pl_action.actionId[1]].timestamp
+ tsp_EPTF_TransportIPL2_tcpConnectionTimeout) {
// notify the app of connection timeout
if(v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].lgenIdx].eventHandler != null) {
var PortError vl_tmp := ERROR_SOCKET;
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].lgenIdx].eventHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].uniqueId,
// Event_Result ,
{result := {
errorCode := vl_tmp,
connId := v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].uniqueId,
os_error_code := 7,//c_EPTF_TransportIPL2_errorConnectionTimedOut, FIXME: use the constant instead of the literal, when the FATAL ERROR of the compiler is fixed
os_error_text := "connection timed out"
}}
);
}
f_EPTF_TransportIPL2_debug(%definitionId&": connection timed out");
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.sendBuffer.segments[pl_action.actionId[1]].retransmitTimerIdx := -1;
f_EPTF_TransportIPL2_finalCloseTcp(pl_action.actionId[0]);
//return false;
return true;
}
f_EPTF_TransportIPL2_sendIpOverL2(
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].remHost,
c_ip_proto_tcp,
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.sendBuffer.segments[pl_action.actionId[1]].encodedSegment,
false, 0, pl_action.actionId[0]);
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.retransmitTime :=
2.0 * v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.retransmitTime;
if(v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.retransmitTime > tsp_EPTF_TransportIPL2_tcpMaxRetransmitTime) {
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.retransmitTime := tsp_EPTF_TransportIPL2_tcpMaxRetransmitTime;
}
if(not f_EPTF_TransportIPL2_scheduleTcpRetransmission(pl_action.actionId[0], pl_action.actionId[1])) {
return false;
}
return true;
}
private function f_EPTF_TransportIPL2_scheduleTcpRetransmission(
in integer pl_socketId,
in integer pl_segmentIdx)
runs on EPTF_TransportIPL2_CT
return boolean
{
if( not f_EPTF_SchedulerComp_scheduleAction(
f_EPTF_SchedulerComp_snapshotTime() + v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime,
refers(f_EPTF_TransportIPL2_retransmitTcpSegment),
{ pl_socketId, pl_segmentIdx },
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[pl_segmentIdx].retransmitTimerIdx)) {
f_EPTF_TransportIPL2_error(%definitionId&": f_EPTF_SchedulerComp_scheduleAction failed, could not schedule retransmission of TCP segment.");
return false;
}
return true;
}
private function f_EPTF_TransportIPL2_cancelTcpRetransmission(
in integer pl_socketId,
in integer pl_segmentIdx)
runs on EPTF_TransportIPL2_CT
{
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[pl_segmentIdx].retransmitTimerIdx >= 0) {
if(not f_EPTF_SchedulerComp_CancelEvent(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[pl_segmentIdx].retransmitTimerIdx)) {
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_CancelEvent failed.");
}
f_EPTF_FBQ_moveFromBusyToFreeTail(pl_segmentIdx, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.queue);
}
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[pl_segmentIdx] := c_EPTF_TransportIPL2_TcpSendSegment_init;
}
friend function f_EPTF_TransportIPL2_hasPendingRetransmission(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
return boolean
{
return 0 != f_EPTF_FBQ_getLengthOfBusyChain(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.queue);
}
friend function f_EPTF_TransportIPL2_forceRetransmission(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
var integer vl_segmentIdx := -1;
if(f_EPTF_FBQ_getBusyHeadIdx(
vl_segmentIdx,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.queue)) {
/* if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_segmentIdx].retransmitTimerIdx >= 0) {
if(not f_EPTF_SchedulerComp_CancelEvent(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_segmentIdx].retransmitTimerIdx)) {
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_CancelEvent failed.");
}
}*/
f_EPTF_TransportIPL2_sendIpOverL2(
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].remHost,
c_ip_proto_tcp,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_segmentIdx].encodedSegment,
false, 0, pl_socketId);
// if(not f_EPTF_TransportIPL2_scheduleTcpRetransmission(pl_socketId, vl_segmentIdx)) {
// }
}
}
private function f_EPTF_TransportIPL2_sendTcpRstOnClosed(
in octetstring pl_srcAddr,
in octetstring pl_dstAddr,
in EPTF_TransportIPL2_PDU_TCP pl_incomingSegment)
runs on EPTF_TransportIPL2_CT
{
var EPTF_TransportIPL2_PDU_TCP vl_pdu := {
source_port := pl_incomingSegment.dest_port,
dest_port := pl_incomingSegment.source_port,
sequence_number := 0,
acknowledgment_number := pl_incomingSegment.sequence_number,
control_bits := c_EPTF_TransportIPL2_TCPControlBit_rst + c_EPTF_TransportIPL2_TCPControlBit_ack,
window := tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize,
options := ''O,
data := ''O
}
if(f_EPTF_and4i(pl_incomingSegment.control_bits, c_EPTF_TransportIPL2_TCPControlBit_ack) != 0) {
vl_pdu.sequence_number := pl_incomingSegment.acknowledgment_number;
}
vl_pdu.acknowledgment_number := vl_pdu.acknowledgment_number + lengthof(pl_incomingSegment.data);
/* var octetstring vl_encPdu := f_enc_PDU_TCP(
pl_dstAddr, // -> becomes source address
pl_srcAddr, // -> becomes destination address
vl_pdu,
true,
true);*/
var octetstring vl_encPdu := f_EPTF_TransportIPL2_encodeTCPWithChecksum(
pl_dstAddr, // -> becomes source address
pl_srcAddr, // -> becomes destination address
vl_pdu);
f_EPTF_TransportIPL2_sendIpOverL2(pl_dstAddr, pl_srcAddr, c_ip_proto_tcp, vl_encPdu, false, 0);
}
private function f_EPTF_TransportIPL2_handleSendTcpAck(
in EPTF_ScheduledAction pl_action, // pl_action.actionId[0]: socket ID
in integer pl_eventIndex)
runs on EPTF_TransportIPL2_CT
return boolean
{
f_EPTF_TransportIPL2_debug(%definitionId&": sending scheduled ACK.");
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.ackTimerIdx := -1;
f_EPTF_TransportIPL2_sendTcpAck(pl_action.actionId[0]);
return true;
}
private function f_EPTF_TransportIPL2_scheduleSendTcpAck(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
if(tsp_EPTF_TransportIPL2_tcpMaxAckDelay < 0.001) {
f_EPTF_TransportIPL2_sendTcpAck(pl_socketId);
return;
}
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.ackTimerIdx < 0) {
// f_EPTF_SchedulerComp_refreshSnapshotTime();
if( not f_EPTF_SchedulerComp_scheduleAction(
f_EPTF_SchedulerComp_snapshotTime() + tsp_EPTF_TransportIPL2_tcpMaxAckDelay,
refers(f_EPTF_TransportIPL2_handleSendTcpAck),
{ pl_socketId },
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.ackTimerIdx)) {
// scheduling failed -> send the ACK
f_EPTF_TransportIPL2_warning(%definitionId&": could not schedule ACK of TCP segment, sending it now.");
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.ackTimerIdx := -1;
f_EPTF_TransportIPL2_sendTcpAck(pl_socketId);
return;
}
// f_EPTF_TransportIPL2_debug(%definitionId&": scheduled ACK.");
} else {
f_EPTF_TransportIPL2_debug(%definitionId&": ACK already scheduled.");
}
}
private function f_EPTF_TransportIPL2_cancelSendTcpAck(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.ackTimerIdx >= 0) {
if(not f_EPTF_SchedulerComp_CancelEvent(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.ackTimerIdx)) {
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_CancelEvent failed.");
}
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.ackTimerIdx := -1;
// f_EPTF_TransportIPL2_debug(%definitionId&": cancelled ACK.");
} else {
f_EPTF_TransportIPL2_debug(%definitionId&": no ACK scheduled.");
}
}
private function f_EPTF_TransportIPL2_sendTcpAck (
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
var EPTF_TransportIPL2_PDU_TCP vl_pdu := {
source_port := v_EPTF_CommPort_connectionDatabase.data[pl_socketId].locPort,
dest_port := v_EPTF_CommPort_connectionDatabase.data[pl_socketId].remPort,
sequence_number := v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendSeqNum,
acknowledgment_number := v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum,
control_bits := c_EPTF_TransportIPL2_TCPControlBit_ack,
window := tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize,//v_EPTF_CommPort_connectionDatabase.data[pl_socketId].receiveWindowSize,
options := ''O,
data := ''O
}
var octetstring vl_encPdu := f_EPTF_TransportIPL2_encodeTCPWithChecksum(
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].remHost,
vl_pdu);
f_EPTF_TransportIPL2_sendIpOverL2(
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].remHost,
c_ip_proto_tcp,
vl_encPdu,
false, 0, pl_socketId);
}
private function f_EPTF_TransportIPL2_handleTimeWaitTimeout(
in EPTF_ScheduledAction pl_action, // pl_action.actionId[0]: socket ID
in integer pl_eventIndex)
runs on EPTF_TransportIPL2_CT
return boolean
{
f_EPTF_TransportIPL2_debug(%definitionId&": closing socket.");
v_EPTF_CommPort_connectionDatabase.data[pl_action.actionId[0]].tcpConnectionData.timeWaitTimerIdx := -1;
f_EPTF_TransportIPL2_finalCloseTcp(pl_action.actionId[0]);
return true;
}
private function f_EPTF_TransportIPL2_scheduleTimeWait(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.timeWaitTimerIdx < 0) {
// f_EPTF_SchedulerComp_refreshSnapshotTime();
if( not f_EPTF_SchedulerComp_scheduleAction(
f_EPTF_SchedulerComp_snapshotTime() + tsp_EPTF_TransportIPL2_tcpMSL * 2.0,
refers(f_EPTF_TransportIPL2_handleTimeWaitTimeout),
{ pl_socketId },
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.timeWaitTimerIdx)) {
// scheduling failed -> close the socket
f_EPTF_TransportIPL2_warning(%definitionId&": could not schedule ACK of TCP segment, sending it now.");
f_EPTF_TransportIPL2_finalCloseTcp(pl_socketId);
return;
}
f_EPTF_TransportIPL2_debug(%definitionId&": scheduled TIME_WAIT.");
} else {
f_EPTF_TransportIPL2_debug(%definitionId&": TIME_WAIT already scheduled.");
}
}
private function f_EPTF_TransportIPL2_cancelTimeWait(
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.timeWaitTimerIdx >= 0) {
if(not f_EPTF_SchedulerComp_CancelEvent(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.timeWaitTimerIdx)) {
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_CancelEvent failed.");
}
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.timeWaitTimerIdx := -1;
f_EPTF_TransportIPL2_debug(%definitionId&": cancelled TIME_WAIT.");
} else {
f_EPTF_TransportIPL2_debug(%definitionId&": no TIME_WAIT scheduled.");
}
}
private function f_EPTF_TransportIPL2_sendTcpFin (
in integer pl_socketId)
runs on EPTF_TransportIPL2_CT
{
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(
pl_socketId,
c_EPTF_TransportIPL2_TCPControlBit_ack + c_EPTF_TransportIPL2_TCPControlBit_fin,
''O);
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.unackedBytes :=
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.unackedBytes +
1;
}
friend function f_EPTF_TransportIPL2_sendTcpSegmentOverL2(
in integer pl_sockId,
in integer pl_flags,
in octetstring pl_segment,
in boolean pl_doSend := true)
runs on EPTF_TransportIPL2_CT
{
if (lengthof(pl_segment) > v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.mss) {
f_EPTF_Base_assert(%definitionId&": pl_segment is longer than MSS for the socket.",
lengthof(pl_segment) <= v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.mss);
}
f_EPTF_SchedulerComp_refreshSnapshotTime();
var EPTF_TransportIPL2_PDU_TCP vl_pdu := {
source_port := v_EPTF_CommPort_connectionDatabase.data[pl_sockId].locPort,
dest_port := v_EPTF_CommPort_connectionDatabase.data[pl_sockId].remPort,
sequence_number := v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum,
acknowledgment_number := v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.expectedSeqNum,
control_bits := pl_flags,
window := tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize,//v_EPTF_CommPort_connectionDatabase.data[pl_sockId].receiveWindowSize,
options := ''O,
data := pl_segment
}
var integer vl_idx := f_EPTF_FBQ_getOrCreateFreeSlot(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.queue);
f_EPTF_FBQ_moveFromFreeToBusyTail(vl_idx, v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.queue);
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.segments[vl_idx].payloadLength := lengthof(pl_segment);
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum :=
f_EPTF_mod32Add(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum, lengthof(pl_segment));
if(f_EPTF_and4i(pl_flags,c_EPTF_TransportIPL2_TCPControlBit_syn) != 0) {
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum :=
f_EPTF_mod32Add(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum, 1);
if(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.mss != c_EPTF_TransportIPL2_tcpDefaultMSS) {
// MSS option
vl_pdu.options := '0204'O & int2oct(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.mss, 2);
}
}
if(f_EPTF_and4i(pl_flags, c_EPTF_TransportIPL2_TCPControlBit_fin) != 0) {
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum :=
f_EPTF_mod32Add(v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum, 1);
}
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.segments[vl_idx].ackSeqNum :=
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendSeqNum;
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.unackedBytes :=
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.unackedBytes +
lengthof(pl_segment);
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.segments[vl_idx].encodedSegment := f_EPTF_TransportIPL2_encodeTCPWithChecksum(
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].remHost,
vl_pdu);
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.segments[vl_idx].timestamp := f_EPTF_SchedulerComp_snapshotTime();
if(not f_EPTF_TransportIPL2_scheduleTcpRetransmission(pl_sockId, vl_idx)) {
return;
}
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.remoteWindowSize :=
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.remoteWindowSize -
lengthof(pl_segment);
if(pl_doSend) {
f_EPTF_TransportIPL2_cancelSendTcpAck(pl_sockId);
f_EPTF_TransportIPL2_sendIpOverL2(
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].locHost,
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].remHost,
c_ip_proto_tcp,
v_EPTF_CommPort_connectionDatabase.data[pl_sockId].tcpConnectionData.sendBuffer.segments[vl_idx].encodedSegment,
false, 0, pl_sockId);
}
}
private function f_EPTF_TransportIPL2_sendIpOverL2(
in EPTF_TransportIPL2_IpAddress pl_locHost,
in EPTF_TransportIPL2_IpAddress pl_remHost,
in integer pl_protoId,
in octetstring pl_payload,
in boolean pl_moreFragments,
in integer pl_fragmentOffset,
in ConnectionId pl_connId := -1)
runs on EPTF_TransportIPL2_CT
{
var IPv4_packet ip_mess := {
header := {
ver := c_ip_version_ipv4,
hlen := 5,
tos := 0,
tlen := lengthof(pl_payload) + 20,
id := v_EPTF_TransportIPL2_ipPacketId,
res := '0'B,
dfrag := '1'B,
mfrag := '0'B,
foffset := pl_fragmentOffset,
ttl := 255,
proto := pl_protoId,
cksum := 0,
srcaddr := pl_locHost,
dstaddr := pl_remHost
},
ext_headers := omit,
payload := pl_payload
}
if (pl_moreFragments){
ip_mess.header.mfrag := '1'B;
}
/* var octetstring data := f_IPv4_enc_eth(ip_mess);
var octetstring chksumip := f_IPv4_checksum( data );
data[10] := chksumip[0];
data[11] := chksumip[1];*/
var octetstring data := f_EPTF_TransportIPL2_encodeIP(ip_mess);
var EPTF_TransportIPL2_MACAddress vl_mac;
var integer vl_ifaceIdx := -1;
if(sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList) > 0 or
tsp_EPTF_TransportIPL2_dstMacAddr == cg_EPTF_TransportIPL2_NullMACAddress) {
// determine destination MAC address from the routing table:
if(not f_EPTF_TransportIPL2_ARP_getMacPrivate(
pl_locHost, // sender
tsp_EPTF_TransportIPL2_srcMacAddr, // sender mac
pl_remHost,
vl_ifaceIdx,
vl_mac)) {
// destination unreachable
f_EPTF_TransportIPL2_warning(%definitionId&": Cannot send data: destination unreachable: "&f_EPTF_TransportIPL2_ip2str(pl_remHost));
return;
}
if(v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ifaceIdx].interfaceId < 0) {
f_EPTF_TransportIPL2_warning(%definitionId&": Cannot send data: interface "&
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ifaceIdx].name&" is not open.");
return;
}
} else {
vl_mac := tsp_EPTF_TransportIPL2_dstMacAddr
}
f_EPTF_TransportIPL2_sendOverL2(
vl_ifaceIdx,
pl_eth_dst_addr := vl_mac,
//pl_eth_src_addr := cg_EPTF_TransportIPL2_NullMACAddress,
pl_type_field := int2oct(c_ip_gre_proto_ipv4, 2),
pl_payload := data,
pl_connId := pl_connId);
}
private function f_EPTF_TransportIPL2_sendOverL2(
in integer pl_interfaceId,
in OCT6 pl_eth_dst_addr,
//in OCT6 pl_eth_src_addr := cg_EPTF_TransportIPL2_NullMACAddress,
in OCT2 pl_type_field,
in PDU_LANL2 pl_payload,
in ConnectionId pl_connId := -1)
runs on EPTF_TransportIPL2_CT
{
//Add 802.1Q VLAN tagging
var integer vl_i := 0;
var octetstring vl_appendMsg := ''O;
if(pl_connId >= 0) {
if(sizeof(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs) > 0)
{
for(vl_i:=0; vl_i<sizeof(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs)-1; vl_i:=vl_i+1)
{
vl_appendMsg := vl_appendMsg & bit2oct(int2bit(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs[vl_i].PCP, 3)&
int2bit(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs[vl_i].CFI, 1)&
int2bit(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs[vl_i].VID, 12)) & c_EPTF_TransportIPL2_VLAN_TPID;
}
//Ethertype has to be set to IPv4 for the last VLAN TCI
vl_appendMsg := vl_appendMsg & bit2oct(int2bit(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs[vl_i].PCP, 3)&
int2bit(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs[vl_i].CFI, 1)&
int2bit(v_EPTF_CommPort_connectionDatabase.data[pl_connId].vlanTCIs[vl_i].VID, 12)) & int2oct(c_ip_gre_proto_ipv4, 2);
pl_type_field := c_EPTF_TransportIPL2_VLAN_TPID;
pl_payload := vl_appendMsg&pl_payload;
}
}
if(pl_interfaceId < 0) {
var ASP_LANL2 vl_message2send := {
eth_dst_addr := pl_eth_dst_addr,
// eth_src_addr := pl_eth_src_addr,
eth_src_addr := omit, // test port determines the source mac address
type_field := pl_type_field,
payload := pl_payload
}
// if (pl_eth_src_addr == cg_EPTF_TransportIPL2_NullMACAddress) {
// vl_message2send.eth_src_addr := omit; // test port determines the source mac address
// }
IPL2_PCO.send(vl_message2send);
} else {
var ASP_v2_LANL2 vl_message2send := {
interface_id := v_EPTF_TransportIPL2_ethernetInterfaceList[pl_interfaceId].interfaceId,
eth_dst_addr := pl_eth_dst_addr,
// eth_src_addr := pl_eth_src_addr,
eth_src_addr := omit, // test port determines the source mac address
type_field := pl_type_field,
payload := pl_payload
}
// if (pl_eth_src_addr == cg_EPTF_TransportIPL2_NullMACAddress) {
// vl_message2send.eth_src_addr := omit; // test port determines the source mac address
// }
IPL2_PCO.send(vl_message2send);
}
}
private function f_EPTF_TransportIPL2_addIp(in octetstring pl_ipAddr)
runs on EPTF_TransportIPL2_CT
{
var integer vl_dummy := -1;
if(not f_EPTF_oct2int_HashMap_Find(v_EPTF_TransportIPL2_ipAddressHashmapId, pl_ipAddr, vl_dummy)) {
f_EPTF_oct2int_HashMap_Insert(v_EPTF_TransportIPL2_ipAddressHashmapId, pl_ipAddr, 1);
}
}
private function f_EPTF_TransportIPL2_hasIp(in octetstring pl_ipAddr)
runs on EPTF_TransportIPL2_CT
return boolean
{
var integer vl_dummy := -1;
return f_EPTF_oct2int_HashMap_Find(v_EPTF_TransportIPL2_ipAddressHashmapId, pl_ipAddr, vl_dummy);
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_registerMsgCallback
//
// Purpose:
// Function to register Message callback function
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - transport type
// pl_LGenType - *in* - *charstring* - LGen type
// pl_msgHandler - *in* - <EPTF_Transport_MsgCallback_FT> - message handler
// pl_eventHandler - *in* - <EPTF_Transport_EventCallback_FT> - event handler
//
// Return Value:
// -
//
// Errors:
// -
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_registerMsgCallback(
in EPTF_Transport_TransportType pl_transportType,
in charstring pl_LGenType,
in EPTF_Transport_MsgCallback_FT pl_msgHandler,
in EPTF_Transport_EventCallback_FT pl_eventHandler := null)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
if (pl_LGenType=="")
{
f_EPTF_TransportIPL2_error(%definitionId&": LGenType must be specified.");
}
else
{
var integer vl_fbqId;
f_EPTF_TransportIPL2_addLGenInfo(pl_LGenType, vl_fbqId);
v_EPTF_TransportIPL2_LGenInfoList[vl_fbqId].msgHandler := pl_msgHandler;
v_EPTF_TransportIPL2_LGenInfoList[vl_fbqId].eventHandler := pl_eventHandler;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_getLocalAddress
//
// Purpose:
// Function to get Local Address for a connection
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> transport type
// pl_connId - *in* - <ConnectionId> - The ID of the connection
// pl_hostName - *out* - <HostName> - local host name
// pl_portNumber - *out* - <PortNumber> - local port numbers
// pl_result - *out* - <Result> - the value of the connection detail
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_getLocalAddress(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
out HostName pl_hostName,
out PortNumber pl_portNumber,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
pl_hostName := f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].locHost);
pl_portNumber := v_EPTF_CommPort_connectionDatabase.data[pl_connId].locPort;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_getRemoteAddress
//
// Purpose:
// Function to get remote Address for a connection
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> transport type
// pl_connId - *in* - <ConnectionId> - The ID of the connection
// pl_hostName - *out* - <HostName> - remote host name
// pl_portNumber - *out* - <PortNumber> - remote port number
// pl_result - *out* - <Result> - the value of the connection detail
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_getRemoteAddress(
in EPTF_Transport_TransportType pl_transportType,
in ConnectionId pl_connId,
out HostName pl_hostName,
out PortNumber pl_portNumber,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
f_EPTF_TransportIPL2_checkConnId(%definitionId, pl_connId);
pl_hostName := f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connId].remHost);
pl_portNumber := v_EPTF_CommPort_connectionDatabase.data[pl_connId].remPort;
}
private function f_EPTF_TransportIPL2_getPacketFilterString(in integer pl_ethInterfaceIdx)
runs on EPTF_TransportIPL2_CT
return charstring
{
return v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.filterString;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_appendInterfaces
//
// Purpose:
// Function to setup additional interfaces
//
// Parameters:
// pl_transportType - *in* <EPTF_Transport_TransportType> - The ID of the connection
// pl_interfaceList - *in* <EPTF_Transport_InterfaceInformationList> - the list of additional interfaces
// pl_result - *out* <Result> - result
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_appendInterfaces(
in EPTF_Transport_TransportType pl_transportType,
in EPTF_Transport_InterfaceInformationList pl_interfaceList,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
if(tsp_EPTF_TransportIPL2_multipleInterfacesMode == false) {
f_EPTF_TransportIPL2_debug(%definitionId&": single interface mode.");
return;
}
var integer vl_nofOldInterfaceInformationElems := sizeof(v_EPTF_Transport_interfaceInformationList);
v_EPTF_Transport_interfaceInformationList := v_EPTF_Transport_interfaceInformationList & pl_interfaceList;
var integer i;
var EPTF_IntegerList vl_changed := {};
for (i := 0; i < sizeof(pl_interfaceList); i := i + 1) {
var integer vl_ethInterfaceIdx := -1;
for(var integer j := 0; j < sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); j:=j+1) {
if(v_EPTF_TransportIPL2_ethernetInterfaceList[j].name == pl_interfaceList[i].ifname) {
vl_ethInterfaceIdx := j;
break;
}
}
if(vl_ethInterfaceIdx < 0) {
// init interface DB elem
vl_ethInterfaceIdx := sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList);
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx] := c_EPTF_TransportIPL2_EthernetInterface_init;
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx].name := pl_interfaceList[i].ifname;
}
var integer vl_nofIdxs := sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx].interfaceInformationIdxs);
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx].interfaceInformationIdxs[vl_nofIdxs] := i + vl_nofOldInterfaceInformationElems;
var integer j;
for(j:=0; j<sizeof(vl_changed) and vl_changed[j]!=vl_ethInterfaceIdx; j:=j+1) { }
vl_changed[j] := vl_ethInterfaceIdx;
}
f_EPTF_TransportIPL2_debug(%definitionId&": filters will be updated for interfaces: "&log2str(vl_changed));
for(i:=0; i<sizeof(vl_changed); i:=i+1) {
var charstring vl_oldFilter := f_EPTF_TransportIPL2_getPacketFilterString(vl_changed[i]);
f_EPTF_TransportIPL2_genPacketFilter(vl_changed[i]);
var charstring vl_newFilter := f_EPTF_TransportIPL2_getPacketFilterString(vl_changed[i]);
if(vl_newFilter != vl_oldFilter) {
if(v_EPTF_TransportIPL2_ethernetInterfaceList[vl_changed[i]].interfaceId >= 0) {
f_EPTF_TransportIPL2_debug(%definitionId&": packet filter changed for interface: "&v_EPTF_TransportIPL2_ethernetInterfaceList[vl_changed[i]].name);
f_EPTF_TransportIPL2_closeInterface(vl_changed[i]);
} else {
f_EPTF_TransportIPL2_debug(%definitionId&": opening new interface: "&v_EPTF_TransportIPL2_ethernetInterfaceList[vl_changed[i]].name);
}
f_EPTF_TransportIPL2_openInterface(vl_changed[i]);
} else {
f_EPTF_TransportIPL2_debug(%definitionId&": packet filter didn't change for interface: "&v_EPTF_TransportIPL2_ethernetInterfaceList[vl_changed[i]].name);
}
}
f_EPTF_TransportIPL2_updateRouteTableToEthIF();
pl_result := c_emptyResult;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_setUpInterfaces
//
// Purpose:
// Function to setup the interfaces
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - The ID of the connection
// pl_result - *out* - <Result> - result
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_setUpInterfaces(
in EPTF_Transport_TransportType pl_transportType,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
if(tsp_EPTF_TransportIPL2_multipleInterfacesMode == false) {
f_EPTF_TransportIPL2_debug(%definitionId&": single interface mode.");
return;
} else {
f_LANL2asp_set_param(IPL2_PCO, "port_mode", "multiple_interface");
}
var integer i;
for (i := 0; i < sizeof(v_EPTF_Transport_interfaceInformationList); i := i + 1) {
var integer vl_ethInterfaceIdx := -1;
for(var integer j := 0; j < sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); j:=j+1) {
if(v_EPTF_TransportIPL2_ethernetInterfaceList[j].name == v_EPTF_Transport_interfaceInformationList[i].ifname) {
vl_ethInterfaceIdx := j;
break;
}
}
if(vl_ethInterfaceIdx < 0) {
// init interface DB elem
vl_ethInterfaceIdx := sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList);
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx] := c_EPTF_TransportIPL2_EthernetInterface_init;
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx].name := v_EPTF_Transport_interfaceInformationList[i].ifname;
}
var integer vl_nofIdxs := sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx].interfaceInformationIdxs);
v_EPTF_TransportIPL2_ethernetInterfaceList[vl_ethInterfaceIdx].interfaceInformationIdxs[vl_nofIdxs] := i;
if(v_EPTF_TransportIPL2_ethernetInterfaceList[i].name == tsp_EPTF_TransportIPL2_loopbackInterface) {
if(tsp_EPTF_TransportIPL2_openLoopbackInterface == false) {
f_EPTF_TransportIPL2_debug(%definitionId&": loopback interface was specified, "&
"overriding tsp_EPTF_TransportIPL2_openLoopbackInterface");
}
v_EPTF_TransportIPL2_openLoopbackInterface := true; // override module parameter if the loopback interface was added explicitly
}
}
f_EPTF_TransportIPL2_initLoopbackInterface();
}
private function f_EPTF_TransportIPL2_isAddressInSubnet(
in EPTF_TransportIPL2_IpAddress pl_addr,
in EPTF_TransportIPL2_IpAddress pl_net,
in EPTF_TransportIPL2_IpAddress pl_mask)
return boolean
{
return pl_net == pl_addr and4b pl_mask;
}
private function f_EPTF_TransportIPL2_nofEqualBits(
in EPTF_TransportIPL2_IpAddress pl_addr1,
in EPTF_TransportIPL2_IpAddress pl_addr2)
return integer
{
var bitstring vl_addr1Bits := oct2bit(pl_addr1);
var bitstring vl_addr2Bits := oct2bit(pl_addr2);
for(var integer i := 0; i < lengthof(vl_addr1Bits); i:=i+1) {
if(vl_addr1Bits[i] != vl_addr2Bits[i]) { return i; }
}
return lengthof(vl_addr1Bits);
}
private function f_EPTF_TransportIPL2_nofBitsInNetmask(
in EPTF_TransportIPL2_IpAddress pl_mask)
return integer
{
return f_EPTF_TransportIPL2_nofEqualBits(pl_mask, c_EPTF_TransportIPL2_singleHostNetmask);
}
private function f_EPTF_TransportIPL2_ipAddrInc(
in EPTF_TransportIPL2_IpAddress pl_addr,
in integer pl_count)
return EPTF_TransportIPL2_IpAddress
{
var integer vl_tmp := oct2int(pl_addr);
vl_tmp := vl_tmp + pl_count;
return int2oct(vl_tmp, 4);
}
private function f_EPTF_TransportIPL2_genNetMask(in integer pl_number)
return EPTF_TransportIPL2_IpAddress {
if(pl_number > 31) { return c_EPTF_TransportIPL2_singleHostNetmask; }
var bitstring vl_mask := ''B;
while(pl_number > 0) {
vl_mask := vl_mask & '1'B;
pl_number := pl_number - 1;
}
while(lengthof(vl_mask) < 32) {
vl_mask := vl_mask & '0'B;
}
return bit2oct(vl_mask);
}
private function f_EPTF_TransportIPL2_genSubnetFilterStr(
in EPTF_TransportIPL2_IpAddress pl_net,
in integer pl_netmaskBits)
return charstring
{
if(pl_netmaskBits >= 32) {
return "dst host " & f_EPTF_TransportIPL2_ip2str(pl_net);
} else {
return "dst net " & f_EPTF_TransportIPL2_ip2str(pl_net) &
"/"&int2str(pl_netmaskBits);
}
}
private function f_EPTF_TransportIPL2_simplifySubnetList(
in EPTF_TransportIPL2_SubnetList pl_subnets)
return EPTF_TransportIPL2_SubnetList
{
var EPTF_TransportIPL2_SubnetList vl_ret := {};
for(var integer i:=0; i<sizeof(pl_subnets); i:=i+1) {
var boolean vl_simplified := false;
for(var integer j:=0; j<sizeof(vl_ret); j:=j+1) {
if(pl_subnets[i] == vl_ret[j]) {
vl_simplified := true;
break;
}
if(pl_subnets[i].maskbits < vl_ret[j].maskbits) {
if(f_EPTF_TransportIPL2_isAddressInSubnet(
pl_addr := vl_ret[j].net,
pl_net := pl_subnets[i].net,
pl_mask := pl_subnets[i].mask)) {
vl_simplified := true;
vl_ret[j] := pl_subnets[i];
break;
}
} else {
if(f_EPTF_TransportIPL2_isAddressInSubnet(
pl_addr := pl_subnets[i].net,
pl_net := vl_ret[j].net,
pl_mask := vl_ret[j].mask)) {
vl_simplified := true;
break;
}
}
}
if(not vl_simplified) {
vl_ret[sizeof(vl_ret)] := pl_subnets[i];
}
}
return vl_ret;
}
private function f_EPTF_TransportIPL2_genFilterStr4Subnets(
in EPTF_TransportIPL2_SubnetList pl_subnets)
return charstring
{
if(sizeof(pl_subnets) == 0) { return "arp or udp or tcp"; }
var charstring vl_ret := "arp or udp or tcp and ";
if(sizeof(pl_subnets) > 1) {
vl_ret := vl_ret & "(";
}
for(var integer i:=0; i<sizeof(pl_subnets); i:=i+1) {
if(pl_subnets[i].maskbits == 0) { return "arp or udp or tcp"; }
if(i>0) {
vl_ret := vl_ret & " or "
}
vl_ret := vl_ret & f_EPTF_TransportIPL2_genSubnetFilterStr(pl_subnets[i].net, pl_subnets[i].maskbits);
}
if(sizeof(pl_subnets) > 1) {
vl_ret := vl_ret & ")";
}
return vl_ret;
}
private function f_EPTF_TransportIPL2_genPacketFilter(in integer pl_ethInterfaceIdx)
runs on EPTF_TransportIPL2_CT
{
if(sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].interfaceInformationIdxs) == 0) {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": skipped, packet filter for interface "&
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].name&": " &
log2str(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter));
}
return;
}
if(sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].interfaceInformationIdxs) == 0) {
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.filterString := "arp or tcp or udp";
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets := {};
return;
}
for(var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].interfaceInformationIdxs); i:=i+1) {
var integer vl_idx := v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].interfaceInformationIdxs[i];
var integer vl_netmaskBits := 32;
var EPTF_TransportIPL2_IpAddress vl_start := f_EPTF_TransportIPL2_getIpFromHostName(v_EPTF_Transport_interfaceInformationList[vl_idx].HostName);
var EPTF_TransportIPL2_IpAddress vl_end := vl_start;
if(v_EPTF_Transport_interfaceInformationList[vl_idx].count > 1) {
vl_end := f_EPTF_TransportIPL2_ipAddrInc(vl_start, v_EPTF_Transport_interfaceInformationList[vl_idx].count - 1);
}
vl_netmaskBits := f_EPTF_TransportIPL2_nofEqualBits(vl_start, vl_end);
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets[i].mask :=
f_EPTF_TransportIPL2_genNetMask(vl_netmaskBits);
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets[i].net :=
vl_start and4b v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets[i].mask;
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets[i].maskbits := vl_netmaskBits;
if(vl_netmaskBits == 0) {
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.filterString := "arp or tcp or udp";
return;
}
}
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets :=
f_EPTF_TransportIPL2_simplifySubnetList(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets);
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.filterString :=
f_EPTF_TransportIPL2_genFilterStr4Subnets(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter.subnets);
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": packet filter for interface "&
v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].name&": " &
log2str(v_EPTF_TransportIPL2_ethernetInterfaceList[pl_ethInterfaceIdx].packetFilter));
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_Transport_setDownInterfaces
//
// Purpose:
// Function to set down interfaces
//
// Parameters:
// pl_transportType - *in* - <EPTF_Transport_TransportType> - Testport type
// pl_result - *out* - <Result> - result
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
//
///////////////////////////////////////////////////////////
public function f_EPTF_Transport_setDownInterfaces(
in EPTF_Transport_TransportType pl_transportType,
out Result pl_result)
runs on EPTF_TransportIPL2_CT
{
if (IPL2 != pl_transportType) {
f_EPTF_Base_assert(%definitionId&": The transport type "&log2str(pl_transportType)&
" is not supported in the IPL2 transport.", IPL2 == pl_transportType)
}
pl_result := { errorCode := omit, connId := omit, os_error_code:=omit, os_error_text:= omit };
//TODO
/* uses IPL4asp_Functions, which uses TCCInterface_Functions.
copy-paste into IPL2 but into where?
var integer vl_numberofinterfaces := sizeof(v_EPTF_CommPort_IPL4_interfaceInformationList);
for (var integer i := 0; i < vl_numberofinterfaces; i := i + 1) {
f_setDownInterface(v_EPTF_CommPort_IPL4_interfaceInformationList[i].ifname,
v_EPTF_CommPort_IPL4_interfaceInformationList[i].count,
v_EPTF_CommPort_IPL4_interfaceInformationList[i].virtualIfaceStart);
}
*/
}
} // end of group EPTF_TransportIPL2_ConnectionHandling
///////////////////////////////////////////////////////////////////////////////
// Group: EPTF_TransportIPL2_IPReassembly
//
// Purpose:
// Functions for reassembly of fragmented IP packets.
//
///////////////////////////////////////////////////////////////////////////////
group EPTF_TransportIPL2_IPReassembly
{
external function f_EPTF_TransportIPL2_insertOctets(
in octetstring pl_buffer,
in octetstring pl_fragment,
in integer pl_offset) return octetstring;
private function f_EPTF_TransportIPL2_removeFragmentBufferEvent(
in EPTF_ScheduledAction pl_action,
in integer pl_eventIndex)
runs on EPTF_TransportIPL2_CT
return boolean
{
if(not f_EPTF_FBQ_itemIsBusy(pl_action.actionId[0], v_EPTF_TransportIPL2_ipFragmentBufferDB.queue)) {
f_EPTF_TransportIPL2_warning(%definitionId&": item "&int2str(pl_action.actionId[0])&" is not busy.");
return false;
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": timeout for buffer with index " & int2str(pl_action.actionId[0]));
}
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[pl_action.actionId[0]].removeTimerIdx := -1;
f_EPTF_TransportIPL2_removeFragmentBuffer(pl_action.actionId[0]);
return true;
}
private function f_EPTF_TransportIPL2_removeFragmentBuffer(in integer pl_idx)
runs on EPTF_TransportIPL2_CT
{
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[pl_idx].msg := ''O;
f_EPTF_FBQ_moveFromBusyToFreeTail(pl_idx, v_EPTF_TransportIPL2_ipFragmentBufferDB.queue);
f_EPTF_oct2int_HashMap_Erase(v_EPTF_TransportIPL2_ipFragmentBufferDB.hashmapId,
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[pl_idx].hashKey);
}
private function f_EPTF_TransportIPL2_incomingIPFragment(
in octetstring pl_srcAddr,
in octetstring pl_dstAddr,
in integer pl_proto,
in integer pl_ipId,
in octetstring pl_msgFragment,
in integer pl_offset,
in boolean pl_isFinal)
runs on EPTF_TransportIPL2_CT
{
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": Incoming IP fragment.");
f_EPTF_TransportIPL2_debug(%definitionId&": srcAddr = " & f_EPTF_TransportIPL2_ip2str(pl_srcAddr));
f_EPTF_TransportIPL2_debug(%definitionId&": dstAddr = " & f_EPTF_TransportIPL2_ip2str(pl_dstAddr));
f_EPTF_TransportIPL2_debug(%definitionId&": ID = " & int2str(pl_ipId));
f_EPTF_TransportIPL2_debug(%definitionId&": fragment length = " & int2str(lengthof(pl_msgFragment)));
f_EPTF_TransportIPL2_debug(%definitionId&": offset = " & int2str(pl_offset));
f_EPTF_TransportIPL2_debug(%definitionId&": isFinal = " & log2str(pl_isFinal));
}
var octetstring vl_key := pl_srcAddr & pl_dstAddr & int2oct(pl_proto, 1) & int2oct(pl_ipId, 2);
var integer vl_idx := -1;
// try to find buffer with key based on source and destination IP address, protocol and ID field
if(not f_EPTF_oct2int_HashMap_Find(v_EPTF_TransportIPL2_ipFragmentBufferDB.hashmapId, vl_key, vl_idx)) {
// not found -> create buffer
vl_idx := f_EPTF_FBQ_getOrCreateFreeSlot(v_EPTF_TransportIPL2_ipFragmentBufferDB.queue);
f_EPTF_FBQ_moveFromFreeToBusyTail(vl_idx, v_EPTF_TransportIPL2_ipFragmentBufferDB.queue);
f_EPTF_oct2int_HashMap_Insert(v_EPTF_TransportIPL2_ipFragmentBufferDB.hashmapId, vl_key, vl_idx);
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": creating new buffer with index " & int2str(vl_idx));
}
// initialize buffer contents:
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx] := {
msg := f_EPTF_TransportIPL2_insertOctets(''O, pl_msgFragment, pl_offset),
receivedBytes := lengthof(pl_msgFragment),
totalBytes := -1, // calculated from final packet's fragment offset and length
removeTimerIdx := -1,
hashKey := vl_key
}
} else {
// found -> update buffer
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": found buffer with index " & int2str(vl_idx));
}
// insert the fragment into the buffer at offset pl_offset
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].msg :=
f_EPTF_TransportIPL2_insertOctets(
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].msg,
pl_msgFragment,
pl_offset);
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].receivedBytes :=
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].receivedBytes + lengthof(pl_msgFragment);
}
if(pl_isFinal) {
// total bytes needed for the whole IP package is the size of the last fragment + its offset
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].totalBytes :=
lengthof(pl_msgFragment) + pl_offset;
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": total bytes needed: " &
int2str(v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].totalBytes));
f_EPTF_TransportIPL2_debug(%definitionId&": bytes in buffer: " &
int2str(v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].receivedBytes));
}
if( v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].receivedBytes ==
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].totalBytes) {
// total bytes == received bytes -> received the complete package
// cancel timer for removing buffer
if(v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].removeTimerIdx >= 0) {
if(not f_EPTF_SchedulerComp_CancelEvent(v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].removeTimerIdx)) {
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_CancelEvent failed.");
}
}
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].removeTimerIdx := -1;
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": received whole IP packet.");
}
// pass the payload to the upper layer
if (pl_proto == c_ip_proto_udp) {
f_EPTF_TransportIPL2_incomingUdpPacket(
pl_srcAddr,
pl_dstAddr,
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].msg);
} else if (pl_proto == c_ip_proto_tcp) {
f_EPTF_TransportIPL2_incomingTcpSegment(
pl_srcAddr,
pl_dstAddr,
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].msg);
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": Unsupported protocol in IP datagram: " & int2str(pl_proto));
}
}
// the buffer is no longer needed -> remove it
f_EPTF_TransportIPL2_removeFragmentBuffer(vl_idx);
} else {
// more fragments are expected
// re-schedule event to remove the buffer if no more fragments are received
f_EPTF_SchedulerComp_refreshSnapshotTime();
if(v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].removeTimerIdx >= 0) {
if(not f_EPTF_SchedulerComp_CancelEvent(v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].removeTimerIdx)) {
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_CancelEvent failed.");
}
}
if( not f_EPTF_SchedulerComp_scheduleAction(
f_EPTF_SchedulerComp_snapshotTime() + tsp_EPTF_TransportIPL2_ipFragmentRemoveTimer,
refers(f_EPTF_TransportIPL2_removeFragmentBufferEvent),
{ vl_idx },
v_EPTF_TransportIPL2_ipFragmentBufferDB.buffers[vl_idx].removeTimerIdx)) {
// scheduling failed -> remove buffer to prevent memory leak
f_EPTF_TransportIPL2_warning(%definitionId&": f_EPTF_SchedulerComp_scheduleAction failed, discarding buffer.");
f_EPTF_TransportIPL2_removeFragmentBuffer(vl_idx);
return;
}
}
}
} // end of group EPTF_TransportIPL2_IPReassembly
group EPTF_TransportIPL2_Layer3 {
private function f_EPTF_TransportIPL2_TransportType_2int(in EPTF_Transport_TransportType pl_e)
return integer
{ return enum2int(pl_e); }
private function f_EPTF_TransportIPL2_incomingUdpPacket(
in octetstring pl_srcAddr,
in octetstring pl_dstAddr,
inout octetstring pl_payload)
runs on EPTF_TransportIPL2_CT
{
var octetstring msg;
var integer srcport, dstport;
if(f_EPTF_TransportIPL2_decodeUDPWithChecksum(
pl_srcAddr,
pl_dstAddr,
pl_payload,
srcport,
dstport,
msg)) {
var integer vl_idx := f_EPTF_TransportIPL2_getConnectionIdx({udp:={}}, pl_dstAddr, dstport);
if (vl_idx != -1) {
var integer vl_lgenIdx := v_EPTF_CommPort_connectionDatabase.data[vl_idx].lgenIdx;
if(vl_lgenIdx >= 0) {
if(v_EPTF_TransportIPL2_LGenInfoList[vl_lgenIdx].msgHandler != null) {
if (f_EPTF_Base_isEnabledDTEHandling()) {
@try{
v_EPTF_TransportIPL2_LGenInfoList[vl_lgenIdx].msgHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[vl_idx].uniqueId,
f_EPTF_TransportIPL2_ip2str(pl_srcAddr),
srcport,
f_EPTF_TransportIPL2_ip2str(pl_dstAddr),
dstport,
{udp:={}},
v_EPTF_CommPort_connectionDatabase.data[vl_idx].userData,
msg
);
} @catch(dte_str) {
f_EPTF_TransportIPL2_warning(log2str(%definitionId&": Dynamic test case error occured during executing message handler ",v_EPTF_TransportIPL2_LGenInfoList[vl_lgenIdx].msgHandler,
" with arguments: ",
" pl_transportType := ", f_EPTF_TransportIPL2_TransportType_2int(IPL2),
" pl_connId := ", v_EPTF_CommPort_connectionDatabase.data[vl_idx].uniqueId,
" pl_remHost := ", f_EPTF_TransportIPL2_ip2str(pl_srcAddr),
" pl_remPort := ", srcport,
" pl_locHost := ", f_EPTF_TransportIPL2_ip2str(pl_dstAddr),
" pl_locPort := ", dstport,
" pl_proto := ", ProtoTuple:{udp:={}},
" pl_userData := ", v_EPTF_CommPort_connectionDatabase.data[vl_idx].userData,
" pl_msg := ", msg,
". Error message: ",dte_str));
}
} else {
v_EPTF_TransportIPL2_LGenInfoList[vl_lgenIdx].msgHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[vl_idx].uniqueId,
f_EPTF_TransportIPL2_ip2str(pl_srcAddr),
srcport,
f_EPTF_TransportIPL2_ip2str(pl_dstAddr),
dstport,
{udp:={}},
v_EPTF_CommPort_connectionDatabase.data[vl_idx].userData,
msg
);
}
}
else { f_EPTF_TransportIPL2_error(%definitionId&": no message handler is set for LGen " & int2str(vl_lgenIdx)); }
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": no LGen is set for connection " &
f_EPTF_TransportIPL2_ip2str(pl_dstAddr) & ":" & int2str(dstport));
}
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": no connection found for incoming UDP packet.");
f_EPTF_TransportIPL2_debug(%definitionId&": source IP Addr: " & f_EPTF_TransportIPL2_ip2str(pl_srcAddr));
f_EPTF_TransportIPL2_debug(%definitionId&": destination IP Addr: " & f_EPTF_TransportIPL2_ip2str(pl_dstAddr));
f_EPTF_TransportIPL2_debug(%definitionId&": UDP header: " & oct2str(substr(pl_payload, 0, 8)));
}
}
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": could not decode UDP PDU.");
}
}
}
private function f_EPTF_TransportIPL2_handleTcpOptions(
in octetstring pl_options,
out integer pl_mss)
runs on EPTF_TransportIPL2_CT
{
var integer i := 0;
while(i<lengthof(pl_options)) {
if(pl_options[i] == '00'O) {
break; // end of options list
} else if(pl_options[i] == '01'O) {
// NOP
i:=i+1;
} else if(pl_options[i] == '02'O) {
if(i+3 >= lengthof(pl_options) or pl_options[i+1] != '04'O) {
f_EPTF_TransportIPL2_debug(%definitionId&": invalid MSS option in incoming SYN segment.");
} else {
pl_mss := oct2int(substr(pl_options, i+2, 2));
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": remote MSS: " & int2str(pl_mss));
}
}
break; // no other option supported at the moment.
} else if(i+1 < lengthof(pl_options)) {
i := i + oct2int(pl_options[i+1]);
} else {
break;
}
}
}
private function f_EPTF_TransportIPL2_handleTcpSyn(
in octetstring pl_srcAddr,
in octetstring pl_dstAddr,
in EPTF_TransportIPL2_PDU_TCP pl_pdu)
runs on EPTF_TransportIPL2_CT
{
var integer vl_listenIdx := f_EPTF_TransportIPL2_getConnectionIdx({tcp:={}}, pl_dstAddr, pl_pdu.dest_port); // get listen socket (source addr == ''O)
if (vl_listenIdx >= 0) {
var integer vl_lgenIdx := v_EPTF_CommPort_connectionDatabase.data[vl_listenIdx].lgenIdx;
if(vl_lgenIdx >= 0) {
var integer vl_connectionIdx := f_EPTF_TransportIPL2_newLocalPort(
{tcp:={}},
pl_dstAddr,
pl_pdu.dest_port,
vl_lgenIdx,
pl_srcAddr,
pl_pdu.source_port);
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.expectedSeqNum := pl_pdu.sequence_number + 1;
// inherit the listener's userData
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].userData := v_EPTF_CommPort_connectionDatabase.data[vl_listenIdx].userData
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(
vl_connectionIdx,
c_EPTF_TransportIPL2_TCPControlBit_syn + c_EPTF_TransportIPL2_TCPControlBit_ack,
''O);
f_EPTF_TransportIPL2_setSocketState(vl_connectionIdx, SYN_RECEIVED);
var integer vl_remoteMss := c_EPTF_TransportIPL2_tcpDefaultMSS;
if(lengthof(pl_pdu.options) > 0) {
f_EPTF_TransportIPL2_handleTcpOptions(pl_pdu.options, vl_remoteMss);
}
if(vl_remoteMss < v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.mss) {
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.mss := vl_remoteMss;
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": local MSS: " &
int2str(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.mss));
}
f_EPTF_TransportIPL2_updateOutgoingStatistics(c_EPTF_TransportIPL2_incConnOpened);
// call event handler of LGen
if(v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].lgenIdx].eventHandler != null) {
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].lgenIdx].eventHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].uniqueId,
// Event_ConnOpened,
{connOpened := {
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].uniqueId,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].remHost),
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].remPort,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].locHost),
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].locPort,
{tcp := {}},
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].userData
}}
);
}
} else {
f_EPTF_TransportIPL2_warning(%definitionId&": no LGen is set for TCP listen socket " &
f_EPTF_TransportIPL2_ip2str(pl_dstAddr) & ":" & int2str(pl_pdu.dest_port));
}
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": no listen socket found for incoming TCP SYN segment: " & log2str(pl_pdu));
}
// send RST segment
// fixme: send if we have any *TCP* connection on this IP address
/* if(f_EPTF_TransportIPL2_hasIp(pl_dstAddr)) {
f_EPTF_TransportIPL2_sendTcpRstOnClosed(
pl_srcAddr,
pl_dstAddr,
pl_pdu);
}*/
}
}
// handle SYN part of SYN+ACK
private function f_EPTF_TransportIPL2_handleTcpSynAck(
in integer pl_connectionIdx,
in EPTF_TransportIPL2_PDU_TCP pl_pdu)
runs on EPTF_TransportIPL2_CT
{
if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == SYN_SENT) {
f_EPTF_TransportIPL2_setSocketState(pl_connectionIdx, ESTABLISHED);
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum := pl_pdu.sequence_number + 1;
var integer vl_remoteMss := c_EPTF_TransportIPL2_tcpDefaultMSS;
if(lengthof(pl_pdu.options) > 0) {
f_EPTF_TransportIPL2_handleTcpOptions(pl_pdu.options, vl_remoteMss);
}
if(vl_remoteMss < v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.mss) {
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.mss := vl_remoteMss;
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": local MSS: " &
int2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.mss));
}
}/* else {
// retransmitted SYN
}*/
f_EPTF_TransportIPL2_sendTcpAck(pl_connectionIdx);
}
private function f_EPTF_TransportIPL2_handleTcpPayload(
in integer pl_connectionIdx,
inout EPTF_TransportIPL2_PDU_TCP pl_pdu)
runs on EPTF_TransportIPL2_CT
{
var integer vl_conc := 0;
if(pl_pdu.sequence_number == v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum) {
// most probably we get the new expected segment
var integer vl_size := 0;
vl_conc := f_EPTF_SegmentBuffer_insertSegment(
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer,
pl_pdu.data,
0, // pl_pdu.sequence_number - v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum
vl_size);
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum :=
f_EPTF_mod32Add(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum, vl_conc);
//f_EPTF_TransportIPL2_scheduleSendTcpAck(pl_connectionIdx);
} else {
var integer vl_start, vl_end;
if(not f_EPTF_TransportIPL2_isSeqNumValid(pl_connectionIdx, pl_pdu.sequence_number, pl_pdu.data, vl_start, vl_end)) {
f_EPTF_TransportIPL2_debug(%definitionId&": segment dropped.");
return;
}
var integer vl_size := 0;
vl_conc := f_EPTF_SegmentBuffer_insertSegment(
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer,
pl_pdu.data,
f_EPTF_mod32Sub(vl_start, v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum),
vl_size);
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum :=
f_EPTF_mod32Add(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum, vl_conc);
//if(vl_conc > 0) { f_EPTF_TransportIPL2_scheduleSendTcpAck(pl_connectionIdx); }
}
var integer vl_seqNumBeforeMsgHandling := v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.sendSeqNum;
if (v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgLenCalc.getMsgLen == null) {
f_EPTF_Base_assert(%definitionId&": no length calculating function was set for LGen.",
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgLenCalc.getMsgLen != null);
}
if (v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgHandler == null) {
f_EPTF_Base_assert(%definitionId&": no message handler function was set for LGen.",
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgHandler != null);
}
// loop while there's data in the continuous buffer
var integer vl_msgLen := v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.msgLen;
var integer vl_contLen := f_EPTF_SegmentBuffer_getContinousLen(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer);
while(vl_contLen > 0) {
var octetstring vl_msg := ''O;
f_EPTF_SegmentBuffer_getBuffer(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer, vl_msg);
if(vl_msgLen <= 0) {
vl_msgLen :=
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgLenCalc.getMsgLen.apply(
IPL2,
vl_msg,
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgLenCalc.getMsgLenArgs
);
if(vl_msgLen <= 0) {
break; // we don't know the length of the message yet
}
}
if( vl_contLen < vl_msgLen) {
break; // there's not enough octets in the buffer yet
}
/* var octetstring vl_msg;
f_EPTF_SegmentBuffer_getBuffer(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer, vl_msg,
vl_msgLen);*/
if(lengthof(vl_msg) > vl_msgLen) { vl_msg := substr(vl_msg, 0, vl_msgLen); }
// pass the message to the message handler:
if (f_EPTF_Base_isEnabledDTEHandling()) {
@try{
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remHost),
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remPort,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locHost),
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locPort,
{tcp:={}},
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].userData,
vl_msg);
} @catch(dte_str) {
f_EPTF_TransportIPL2_warning(log2str(%definitionId&": Dynamic test case error occured during executing message handler ",v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgHandler,
" with arguments: ",
" pl_transportType := ", f_EPTF_TransportIPL2_TransportType_2int(IPL2),
" pl_connId := ", v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
" pl_remHost := ", f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remHost),
" pl_remPort := ", v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remPort,
" pl_locHost := ", f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locHost),
" pl_locPort := ", v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locPort,
" pl_proto := ", ProtoTuple:{tcp:={}},
" pl_userData := ", v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].userData,
" pl_msg := ", vl_msg,
". Error message: ",dte_str));
}
} else {
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].msgHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remHost),
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remPort,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locHost),
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locPort,
{tcp:={}},
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].userData,
vl_msg);
}
// remove the message from the buffer:
f_EPTF_SegmentBuffer_truncBuffer(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer,
vl_msgLen);
vl_msgLen := -1;
vl_contLen := f_EPTF_SegmentBuffer_getContinousLen(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer);
}
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.msgLen := vl_msgLen;
if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer.finSegmentOffset == 0) {
// a FIN segment was buffered, receive window became empty -> handle FIN
f_EPTF_TransportIPL2_doHandleTcpFin(pl_connectionIdx); // sends ACK if needed
} else if(vl_conc > 0 and
vl_seqNumBeforeMsgHandling == v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.sendSeqNum) { // only schedule the ACK if the message handler didn't send anything
f_EPTF_TransportIPL2_scheduleSendTcpAck(pl_connectionIdx);
}
}
private function f_EPTF_TransportIPL2_doHandleTcpFin(
in integer pl_connectionIdx)
runs on EPTF_TransportIPL2_CT
{
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum := f_EPTF_mod32Add(
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum, 1); // ACK for the incoming FIN = last seq num + 1
if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == FIN_WAIT2) {
f_EPTF_TransportIPL2_setSocketState(pl_connectionIdx, TIME_WAIT);
// schedule final close of socket
f_EPTF_TransportIPL2_scheduleTimeWait(pl_connectionIdx);
f_EPTF_TransportIPL2_sendTcpAck(pl_connectionIdx);
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == FIN_WAIT1) {
f_EPTF_TransportIPL2_setSocketState(pl_connectionIdx, CLOSING);
f_EPTF_TransportIPL2_sendTcpAck(pl_connectionIdx);
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == ESTABLISHED) {
f_EPTF_TransportIPL2_setSocketState(pl_connectionIdx, CLOSE_WAIT);
// notify the applib of the remote close, wait for it to close too, unless tsp_EPTF_TransportIPL2_tcpAllowHalfClose is false
if(v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].eventHandler != null) {
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].eventHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
// Event_ConnClosed,
{connClosed := {
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remHost),
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].remPort,
f_EPTF_TransportIPL2_ip2str(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locHost),
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].locPort,
{tcp := {}},
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].userData
}}
);
}
if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == CLOSE_WAIT) {
if(tsp_EPTF_TransportIPL2_tcpAllowHalfClose) {
// if the app didn't close the socket and half-closed is allowed - schedule an ACK
f_EPTF_TransportIPL2_scheduleSendTcpAck(pl_connectionIdx);
} else {
var Result vl_dummy;
f_EPTF_Transport_close(IPL2, pl_connectionIdx, vl_dummy);
f_EPTF_TransportIPL2_debug(%definitionId&": half-closed sockets not allowed. Closing socket.");
}
}
}
}
private function f_EPTF_TransportIPL2_handleTcpFin(
in integer pl_connectionIdx,
in EPTF_TransportIPL2_PDU_TCP pl_pdu)
runs on EPTF_TransportIPL2_CT
{
if(f_EPTF_SegmentBuffer_getNonContinousLen(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer) == 0) {
// receive window is empty, no more data is expected -> handle FIN immediately
if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer.finSegmentOffset == 0) {
f_EPTF_TransportIPL2_debug(%definitionId&": retransmitted FIN.");
f_EPTF_TransportIPL2_sendTcpAck(pl_connectionIdx);
} else {
f_EPTF_SegmentBuffer_setFinalSegmentOffset(
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer, 0); // don't accept more data
f_EPTF_TransportIPL2_doHandleTcpFin(pl_connectionIdx); // sends ACK if needed
}
} else {
// buffer FIN segment
var integer vl_dataLen := lengthof(pl_pdu.data);
f_EPTF_SegmentBuffer_setFinalSegmentOffset(
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.segmentBuffer,
f_EPTF_mod32Sub(pl_pdu.sequence_number, v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpConnectionData.expectedSeqNum) + vl_dataLen);
}
}
private function f_EPTF_TransportIPL2_handleTcpRst(
in integer pl_connectionIdx,
in EPTF_TransportIPL2_PDU_TCP pl_pdu)
runs on EPTF_TransportIPL2_CT
{
// check seq and ack numbers
// we assume ack number was already checked, check the sequence number
if(v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == SYN_SENT or
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].tcpState == SYN_RECEIVED) {
if(pl_pdu.sequence_number != 0) { return; }
} else {
var integer vl_start := -1, vl_end := -1;
var octetstring vl_pdu := ''O;
if(not f_EPTF_TransportIPL2_isSeqNumValid(pl_connectionIdx, pl_pdu.sequence_number, vl_pdu, vl_start, vl_end)) {
return;
}
}
f_EPTF_TransportIPL2_debug(%definitionId&": connection reset by peer.");
// notify the app
if(v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].eventHandler != null) {
var PortError vl_tmp := ERROR_SOCKET;
v_EPTF_TransportIPL2_LGenInfoList[v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].lgenIdx].eventHandler.apply(
IPL2,
v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
// Event_Result,
{result := {
errorCode := vl_tmp,
connId := v_EPTF_CommPort_connectionDatabase.data[pl_connectionIdx].uniqueId,
os_error_code := 8,//c_EPTF_TransportIPL2_errorConnectionReset, FIXME: use the constant instead of the literal, when the FATAL ERROR of the compiler is fixed
os_error_text := "connection reset by peer"
}}
);
}
// close the connection
f_EPTF_TransportIPL2_finalCloseTcp(pl_connectionIdx);
}
private function f_EPTF_TransportIPL2_incomingTcpSegment(
in octetstring pl_srcAddr,
in octetstring pl_dstAddr,
inout octetstring pl_payload)
runs on EPTF_TransportIPL2_CT
{
if(not f_EPTF_TransportIPL2_verifyTCPChecksum(pl_srcAddr, pl_dstAddr, pl_payload)) {
f_EPTF_TransportIPL2_debug(%definitionId&": TCP checksum verification failed for incoming segment.");
return;
}
var EPTF_TransportIPL2_PDU_TCP vl_pdu;
f_EPTF_TransportIPL2_decodeTCP(pl_payload, vl_pdu);
var integer vl_unAcked := 0;
f_EPTF_SchedulerComp_refreshSnapshotTime();
var integer vl_connectionIdx := f_EPTF_TransportIPL2_getConnectionIdx(
{tcp:={}},
pl_dstAddr, vl_pdu.dest_port,
pl_srcAddr, vl_pdu.source_port);
if(vl_connectionIdx < 0) {
// connection does not exist
if(f_EPTF_and4i(vl_pdu.control_bits, c_EPTF_TransportIPL2_TCPControlBit_syn) != 0) {
// incoming connection
// 2nd step of 3-way handshake -> create new connection, send SYN+ACK
f_EPTF_TransportIPL2_handleTcpSyn(pl_srcAddr, pl_dstAddr, vl_pdu);
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": no connection found for incoming TCP segment: " & log2str(vl_pdu));
}
// send RST segment, *unless* it's just an ACK segment, or if we don't have this IP address (don't send RSTs on connections handled by the kernel)
// fixme: send if we have any *TCP* connection on this IP address
/* if((vl_pdu.control_bits != c_EPTF_TransportIPL2_TCPControlBit_ack or
lengthof(vl_pdu.data)>0) and
f_EPTF_TransportIPL2_hasIp(pl_dstAddr)) {
f_EPTF_TransportIPL2_sendTcpRstOnClosed(
pl_srcAddr,
pl_dstAddr,
vl_pdu);
}*/
}
} else {
// connection exists
// handle ACK
if(f_EPTF_and4i(vl_pdu.control_bits, c_EPTF_TransportIPL2_TCPControlBit_ack) != 0) {
if(not f_EPTF_TransportIPL2_isAckValid(vl_connectionIdx, vl_pdu.acknowledgment_number)) {
f_EPTF_TransportIPL2_debug(%definitionId&": segment dropped.");
return;
} else {
vl_unAcked := f_EPTF_TransportIPL2_handleTcpAck(vl_connectionIdx, vl_pdu.acknowledgment_number);
if(vl_unAcked < 0) { return; } // LAST_ACK -> CLOSED
}
}
if(f_EPTF_and4i(vl_pdu.control_bits, c_EPTF_TransportIPL2_TCPControlBit_rst) != 0) {
// connection reset by peer
f_EPTF_TransportIPL2_handleTcpRst(vl_connectionIdx, vl_pdu)
return; // no data is expected in a RST segment
}
// handle SYN part of SYN+ACK
if(f_EPTF_and4i(vl_pdu.control_bits, c_EPTF_TransportIPL2_TCPControlBit_syn) != 0) {
// this was an outgoing connection
// 3rd step of 3-way handshake-> connection established, send ACK on incoming SYN
f_EPTF_TransportIPL2_handleTcpSynAck(vl_connectionIdx, vl_pdu);
}
}
if(vl_connectionIdx >= 0) {
// connections exists (could be just created in the previous if branch, thus the separate if statement)
if(lengthof(vl_pdu.data) > 0) {
f_EPTF_TransportIPL2_handleTcpPayload(vl_connectionIdx, vl_pdu);
}
if(f_EPTF_and4i(vl_pdu.control_bits, c_EPTF_TransportIPL2_TCPControlBit_fin) != 0) {
f_EPTF_TransportIPL2_handleTcpFin(vl_connectionIdx, vl_pdu);
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": unacknowledged outgoing bytes: "&int2str(vl_unAcked));
f_EPTF_TransportIPL2_debug(%definitionId&": remote receive window: " &int2str(vl_pdu.window));
if(vl_unAcked > 0) {
f_EPTF_TransportIPL2_debug(%definitionId&": remote receive window decreased: " &int2str(vl_pdu.window - vl_unAcked));
}
}
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.remoteWindowSize := vl_pdu.window - vl_unAcked;
// if there's anything in the send FIFO and the window is non-zero, try to send it
var integer vl_fifoLength;
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
vl_fifoLength := f_EPTF_Buffer_get_read_len(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID);
} else {
vl_fifoLength := lengthof(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo);
}
var integer vl_fifoOffset := 0;
if(vl_fifoLength > 0 and
(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpState == ESTABLISHED or
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpState == CLOSE_WAIT)) {
var integer vl_mss := v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.mss;
var integer vl_flags := c_EPTF_TransportIPL2_TCPControlBit_ack;
do {
if(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.remoteWindowSize < vl_mss) {
if(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.remoteWindowSize > 0) {
vl_mss := v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.remoteWindowSize; // fit the segment in the window
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": remote window full, "&
int2str(vl_fifoLength)&" bytes remained in the fifo.");
}
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
f_EPTF_Buffer_set_pos(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID, vl_fifoOffset);
f_EPTF_Buffer_cut(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID);
} else {
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo :=
substr(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo,
vl_fifoOffset,
lengthof(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo) - vl_fifoOffset);
}
break;
}
}
if (vl_fifoLength <= vl_mss){
vl_flags := vl_flags + c_EPTF_TransportIPL2_TCPControlBit_psh;
if(vl_fifoOffset > 0) {
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
f_EPTF_Buffer_set_pos(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID, vl_fifoOffset);
f_EPTF_Buffer_cut(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID);
} else {
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo :=
substr(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo,
vl_fifoOffset,
lengthof(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo) - vl_fifoOffset);
}
}
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
var octetstring vl_sendFifo;
f_EPTF_Buffer_get_read_data(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID, vl_sendFifo);
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(vl_connectionIdx, vl_flags, vl_sendFifo);
} else {
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(vl_connectionIdx, vl_flags, v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo);
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": sent "&int2str(vl_fifoLength)&" bytes from send FIFO, FIFO is now empty.");
}
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
f_EPTF_Buffer_clear(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID);
} else {
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo := ''O;
}
if(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.pendingClose) {
v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.pendingClose := false;
f_EPTF_TransportIPL2_debug(%definitionId&": close pending, calling close().");
var Result vl_res;
f_EPTF_Transport_close(IPL2, vl_connectionIdx, vl_res);
}
break;
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": sending "&int2str(vl_mss)&" bytes from send FIFO.");
}
if(tsp_EPTF_TransportIPL2_sendFifo_useCLLBuffer){
var octetstring vl_sendFifo;
f_EPTF_Buffer_get_read_data(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo_bufferID, vl_sendFifo);
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(vl_connectionIdx, vl_flags, substr(vl_sendFifo, vl_fifoOffset, vl_mss ));
} else {
f_EPTF_TransportIPL2_sendTcpSegmentOverL2(vl_connectionIdx, vl_flags, substr(v_EPTF_CommPort_connectionDatabase.data[vl_connectionIdx].tcpConnectionData.sendFifo, vl_fifoOffset, vl_mss ));
}
vl_fifoOffset := vl_fifoOffset + vl_mss;
vl_fifoLength := vl_fifoLength - vl_mss;
}
} while(true);
}
}
}
private function f_EPTF_TransportIPL2_isSeqNumValid(
in integer pl_socketId,
in integer pl_seqNum,
inout octetstring pl_pduData,
out integer pl_validStart,
out integer pl_validEnd)
runs on EPTF_TransportIPL2_CT
return boolean
{
/* if(pl_seqNum == v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum) {
pl_validStart := pl_seqNum;
pl_validEnd := f_EPTF_mod32Add(pl_seqNum, pl_payloadLength);
f_EPTF_TransportIPL2_debug(%definitionId&": segment sequence number equals start of window.");
return true; -- not needed, handled in a different if branch
}*/
var integer vl_lastSeqNum := f_EPTF_mod32Add(pl_seqNum, lengthof(pl_pduData) - 1);
var integer vl_endOfWindow := v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum;
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.segmentBuffer.finSegmentOffset >= 0) {
vl_endOfWindow := f_EPTF_mod32Add(vl_endOfWindow,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.segmentBuffer.finSegmentOffset);
} else {
vl_endOfWindow := f_EPTF_mod32Add(vl_endOfWindow,
// v_EPTF_CommPort_connectionDatabase.data[pl_socketId].receiveWindowSize - 1);
tsp_EPTF_TransportIPL2_tcpDefaultReceiveWindowSize - 1);
}
if(f_EPTF_mod32Less(vl_lastSeqNum, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum) or
f_EPTF_mod32Greater(pl_seqNum, vl_endOfWindow)) {
f_EPTF_TransportIPL2_debug(%definitionId&": segment falls outside the receive window.");
return false;
} else if(lengthof(pl_pduData) == 0) {
pl_validStart := pl_seqNum;
pl_validEnd := pl_seqNum;
return true;
}
if(f_EPTF_mod32Less(pl_seqNum, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum)) {
var integer vl_trunc := f_EPTF_mod32Sub(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum, pl_seqNum);
pl_pduData := substr(pl_pduData, vl_trunc, lengthof(pl_pduData) - vl_trunc);
pl_validStart := v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.expectedSeqNum;
f_EPTF_TransportIPL2_debug(%definitionId&": segment overlaps with start of window.");
} else {
pl_validStart := pl_seqNum;
}
if(f_EPTF_mod32Greater(vl_lastSeqNum, vl_endOfWindow)) {
var integer vl_trunc := f_EPTF_mod32Sub(vl_lastSeqNum, vl_endOfWindow);
pl_pduData := substr(pl_pduData, 0, lengthof(pl_pduData) - vl_trunc);
pl_validEnd := vl_endOfWindow;
f_EPTF_TransportIPL2_debug(%definitionId&": segment overlaps with end of window.");
} else {
pl_validEnd := vl_lastSeqNum;
}
return true;
}
private function f_EPTF_TransportIPL2_isAckValid(
in integer pl_socketId,
in integer pl_ack)
runs on EPTF_TransportIPL2_CT
return boolean
{
var integer vl_lastValidAck := f_EPTF_mod32Add(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendWindowStart,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.unackedBytes + 1);
if(f_EPTF_mod32GreaterOrEqual(pl_ack, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendWindowStart) and
f_EPTF_mod32LessOrEqual(pl_ack, vl_lastValidAck)) {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": ack: "&int2str(pl_ack)&
", send window start: "&int2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendWindowStart)&
", last valid ack: "&int2str(vl_lastValidAck));
}
return true;
} else {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": invalid ack "&int2str(pl_ack)&
", send window start: "&int2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendWindowStart)&
", last valid ack: "&int2str(vl_lastValidAck));
}
return false;
}
}
private function f_EPTF_TransportIPL2_handleTcpAck(
in integer pl_socketId,
in integer pl_ack)
runs on EPTF_TransportIPL2_CT
return integer // number of non acked payload bytes or -1 if the socket was closed (LAST_ACK -> CLOSED)
{
// find out if the ack is valid
// if(not f_EPTF_TransportIPL2_isAckValid(pl_socketId, pl_ack)) { return; }
var integer vl_iter;
var integer vl_unAcked := 0;
if(f_EPTF_FBQ_getBusyHeadIdx(vl_iter, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.queue)) {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": incoming ack: "&int2str(pl_ack));
}
var boolean vl_loop := true;
var integer vl_next := vl_iter;
do {
vl_loop := f_EPTF_FBQ_getFwdBusyItemIdx(vl_next, v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.queue);
if(f_EPTF_mod32GreaterOrEqual(pl_ack,
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_iter].ackSeqNum)) {
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": segment acked: "&
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_iter]));
}
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.unackedBytes :=
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.unackedBytes -
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_iter].payloadLength;
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendWindowStart :=
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_iter].ackSeqNum;
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": send window start updated to "&
int2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendWindowStart));
}
if(tsp_EPTF_TransportIPL2_tcpCalculateRTT) {
// round trip time:
var float vl_rtt := f_EPTF_SchedulerComp_snapshotTime() -
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_iter].timestamp;
if( v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState == SYN_RECEIVED or
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState == SYN_SENT) {
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.smoothedRTT := vl_rtt;
} else {
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.smoothedRTT :=
tsp_EPTF_TransportIPL2_tcpSmoothAlpha * v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.smoothedRTT +
(1.0 - tsp_EPTF_TransportIPL2_tcpSmoothAlpha) * vl_rtt;
}
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime :=
tsp_EPTF_TransportIPL2_tcpSmoothBeta *v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.smoothedRTT;
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime < tsp_EPTF_TransportIPL2_tcpMinRetransmitTime) {
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime := tsp_EPTF_TransportIPL2_tcpMinRetransmitTime;
} else if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime > tsp_EPTF_TransportIPL2_tcpMaxRetransmitTime) {
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime := tsp_EPTF_TransportIPL2_tcpMaxRetransmitTime;
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": round trip time: "& float2str(vl_rtt));
f_EPTF_TransportIPL2_debug(%definitionId&": smoothed round trip time: "& float2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.smoothedRTT));
f_EPTF_TransportIPL2_debug(%definitionId&": retransmit time: "& float2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime));
}
} else {
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.retransmitTime := tsp_EPTF_TransportIPL2_tcpMinRetransmitTime;
}
f_EPTF_TransportIPL2_cancelTcpRetransmission(pl_socketId, vl_iter);
} else {
// the remote receive window size must be decreased with the unacknowledged segments
// because these segments may be retransmitted (unless acked in a subsequent incoming segment)
vl_unAcked := vl_unAcked +
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpConnectionData.sendBuffer.segments[vl_iter].payloadLength;
}
vl_iter := vl_next;
} while(vl_loop);
}
// note: skip the select/case (i.e. if/elseif/else) in the most common case, the ESTABLISHED state
if(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState != ESTABLISHED) {
select(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState) {
case(SYN_RECEIVED) {
f_EPTF_TransportIPL2_setSocketState(pl_socketId, ESTABLISHED);
}
case(FIN_WAIT1) {
if(vl_unAcked == 0) { // everything acked - including the FIN
// in this state transition, the retransmission of the FIN we sent is cancelled, but we still wait for the FIN of the peer
// the FIN may be in the same incoming segment, and will be handled after this ACK in f_EPTF_TransportIPL2_incomingTcpSegment
f_EPTF_TransportIPL2_setSocketState(pl_socketId, FIN_WAIT2);
}
}
case(CLOSING) {
if(vl_unAcked == 0) { // everything acked - including the FIN
// in this state transition, the retransmission of the FIN we sent is cancelled
// we wait for some time before finally closing the socket, so we can still ack incoming retransmitted segments
f_EPTF_TransportIPL2_setSocketState(pl_socketId, TIME_WAIT);
// schedule final close of socket
f_EPTF_TransportIPL2_scheduleTimeWait(pl_socketId);
}
}
case(LAST_ACK) {
// we got the ACK for our part of the FIN in passive close -> final-close the socket
f_EPTF_TransportIPL2_finalCloseTcp(pl_socketId);
return -1;
}
case else { }
}
}
return vl_unAcked;
}
private function f_EPTF_TransportIPL2_setSocketState(
in integer pl_socketId,
in EPTF_TransportIPL2_TCPState pl_newState)
runs on EPTF_TransportIPL2_CT
{
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": state changed for socket "&int2str(pl_socketId)&
" from "&log2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState)&
" to "&log2str(pl_newState));
if(not match({v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState, pl_newState}, t_EPTF_TransportIPL2_validStateTransitions)) {
f_EPTF_TransportIPL2_warning(%definitionId&": invalid socket state transition " &
log2str(v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState) &
" -> " & log2str(pl_newState));
}
}
v_EPTF_CommPort_connectionDatabase.data[pl_socketId].tcpState := pl_newState;
}
} // group EPTF_TransportIPL2_Layer3
group EPTF_TransportIPL2_SegmentBuffer
{
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_initBuffer
//
// Purpose:
// (Re-)Initializes the segment buffer
//
// Parameters:
// - pl_buffer - *inout* <EPTF_SegmentBuffer> - the segment buffer
//
// Purpose:
// Initializes a new segment buffer. Can also be called to clear/reset the whole buffer.
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_initBuffer(
inout EPTF_SegmentBuffer pl_buffer)
{
pl_buffer := c_EPTF_SegmentBuffer_init;
f_EPTF_FBQ_initFreeBusyQueue(pl_buffer.nonContSegments.queue);
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
// TODO kell-e vajon a vegere pozicionalni? : f_EPTF_Buffer_set_pos(pl_buffer.continuousBufferID, ???);
pl_buffer.continuousBufferID := f_EPTF_Buffer_new();
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_init_CT
//
// Purpose:
// (Re-)Initializes the segment buffer component
//
// Parameters:
// - pl_selfName - *in* <charstring> - name of the component
//
// Purpose:
// Initializes the segment buffer component.
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_init_CT(
in charstring pl_selfName)
runs on EPTF_SegmentBuffer_CT
{
f_EPTF_Buffer_init_CT(pl_selfName);
f_EPTF_FBQ_init_CT(pl_selfName);
}
private function f_EPTF_SegmentBuffer_logSegments(
in charstring pl_caller,
inout EPTF_SegmentBuffer pl_buffer)
{
if(c_EPTF_Common_debugSwitch and tsp_EPTF_TransportIPL2_enableLogging) {
var integer vl_iter;
if(f_EPTF_FBQ_getBusyHeadIdx(vl_iter, pl_buffer.nonContSegments.queue)) {
f_EPTF_Common_user(pl_caller&": Segment buffer:");
do {
f_EPTF_Common_user(pl_caller&": "&log2str(pl_buffer.nonContSegments.segments[vl_iter]));
} while(f_EPTF_FBQ_getFwdBusyItemIdx(vl_iter, pl_buffer.nonContSegments.queue));
} else {
f_EPTF_Common_user(pl_caller&": Segment buffer is empty.");
}
if(pl_buffer.finSegmentOffset < 0) {
f_EPTF_Common_user(pl_caller&": final segment not yet received.");
} else if(pl_buffer.finSegmentOffset == 0) {
f_EPTF_Common_user(pl_caller&": final segment handled.");
} else {
f_EPTF_Common_user(pl_caller&": final segment offset " & int2str(pl_buffer.finSegmentOffset));
}
}
}
public function f_EPTF_SegmentBuffer_setFinalSegmentOffset(
inout EPTF_SegmentBuffer pl_buffer,
in integer pl_finSegmentOffset)
// return integer
{
// if(pl_buffer.finSegmentOffset < 0) {
pl_buffer.finSegmentOffset := pl_finSegmentOffset;
if(c_EPTF_Common_debugSwitch and tsp_EPTF_TransportIPL2_enableLogging) {
f_EPTF_Common_user(%definitionId&": final segment offset set to "&int2str(pl_finSegmentOffset));
}
/* } else {
f_EPTF_Common_warning(%definitionId&": final segment offset already set: ", pl_buffer.finSegmentOffset);
}*/
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_insertSegment
//
// Purpose:
// Inserts a new segment into the buffer of the open socket
//
// Parameters:
// - pl_buffer - *inout* <EPTF_SegmentBuffer> - the segment buffer
// - pl_payload - *in* *octetstring* - the string to be inserted
// - pl_seqNum - *in* *integer* - sequence number of the segment, relative from the start of the window
// - pl_size - *out* *integer* - the size of the continuous part
// Return Value:
// *integer* - the number of bytes concatenated to the continuous part
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_insertSegment(
inout EPTF_SegmentBuffer pl_buffer,
in octetstring pl_payload,
in integer pl_seqNum,
out integer pl_size)
return integer
{
var integer vl_iter;
if (pl_seqNum == 0){
var integer vl_payloadLen := lengthof(pl_payload);
if(pl_buffer.finSegmentOffset > 0 and vl_payloadLen > pl_buffer.finSegmentOffset) {
f_EPTF_Common_warning(%definitionId&": incoming segment has data with segment number greater than the FIN segment. ");
f_EPTF_Common_warning(%definitionId&": Length of data: "&int2str(vl_payloadLen));
f_EPTF_Common_warning(%definitionId&": FIN segment offset: "&int2str(pl_buffer.finSegmentOffset));
vl_payloadLen := pl_buffer.finSegmentOffset;
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
// TODO kell-e vajon a vegere pozicionalni? : f_EPTF_Buffer_set_pos(pl_buffer.continuousBufferID, ???);
f_EPTF_Buffer_put_os(pl_buffer.continuousBufferID, substr(pl_payload, 0, vl_payloadLen));
} else {
pl_buffer.continuousBuffer := pl_buffer.continuousBuffer&substr(pl_payload, 0, vl_payloadLen);
}
} else {
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
// TODO kell-e vajon a vegere pozicionalni? : f_EPTF_Buffer_set_pos(pl_buffer.continuousBufferID, ???);
f_EPTF_Buffer_put_os(pl_buffer.continuousBufferID, pl_payload);
} else {
pl_buffer.continuousBuffer := pl_buffer.continuousBuffer&pl_payload;
}
}
var integer vl_concatenated := vl_payloadLen;
if(c_EPTF_Common_debugSwitch and tsp_EPTF_TransportIPL2_enableLogging) {
f_EPTF_Common_user(%definitionId&": Concatenated "&int2str(vl_payloadLen)&" bytes of payload to continuous buffer.");
}
if(f_EPTF_FBQ_getBusyHeadIdx(vl_iter, pl_buffer.nonContSegments.queue)) {
var boolean vl_loop := true;
var integer vl_next := vl_iter;
do {
vl_loop := f_EPTF_FBQ_getFwdBusyItemIdx(vl_next, pl_buffer.nonContSegments.queue);
pl_buffer.nonContSegments.segments[vl_iter].seqNum :=
pl_buffer.nonContSegments.segments[vl_iter].seqNum - vl_concatenated;
if(pl_buffer.nonContSegments.segments[vl_iter].seqNum <= 0) {
if(pl_buffer.nonContSegments.segments[vl_iter].seqNum == 0) {
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
// TODO kell-e vajon a vegere pozicionalni? : f_EPTF_Buffer_set_pos(pl_buffer.continuousBufferID, ???);
f_EPTF_Buffer_put_os(pl_buffer.continuousBufferID, pl_buffer.nonContSegments.segments[vl_iter].data);
} else {
pl_buffer.continuousBuffer := pl_buffer.continuousBuffer&pl_buffer.nonContSegments.segments[vl_iter].data;
}
vl_concatenated := vl_concatenated + lengthof(pl_buffer.nonContSegments.segments[vl_iter].data);
} else if((lengthof(pl_buffer.nonContSegments.segments[vl_iter].data) + pl_buffer.nonContSegments.segments[vl_iter].seqNum) > 0) {
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
// TODO kell-e vajon a vegere pozicionalni? : f_EPTF_Buffer_set_pos(pl_buffer.continuousBufferID, ???);
f_EPTF_Buffer_put_os(pl_buffer.continuousBufferID,
substr(pl_buffer.nonContSegments.segments[vl_iter].data,
-pl_buffer.nonContSegments.segments[vl_iter].seqNum,
lengthof(pl_buffer.nonContSegments.segments[vl_iter].data) + pl_buffer.nonContSegments.segments[vl_iter].seqNum));
} else {
pl_buffer.continuousBuffer := pl_buffer.continuousBuffer&
substr(pl_buffer.nonContSegments.segments[vl_iter].data,
-pl_buffer.nonContSegments.segments[vl_iter].seqNum,
lengthof(pl_buffer.nonContSegments.segments[vl_iter].data) + pl_buffer.nonContSegments.segments[vl_iter].seqNum);
}
vl_concatenated := vl_concatenated + lengthof(pl_buffer.nonContSegments.segments[vl_iter].data) + pl_buffer.nonContSegments.segments[vl_iter].seqNum;
}
pl_buffer.nonContSegments.nofBytes :=
pl_buffer.nonContSegments.nofBytes - lengthof(pl_buffer.nonContSegments.segments[vl_iter].data);
pl_buffer.nonContSegments.segments[vl_iter].data := ''O;
f_EPTF_FBQ_moveFromBusyToFreeTail(vl_iter, pl_buffer.nonContSegments.queue);
}
vl_iter := vl_next;
} while(vl_loop);
if(c_EPTF_Common_debugSwitch and tsp_EPTF_TransportIPL2_enableLogging) {
if(vl_concatenated > vl_payloadLen) {
f_EPTF_Common_user(%definitionId&": Concatenated "&int2str(vl_concatenated-vl_payloadLen)&" bytes from segment buffer to continuous buffer.");
} else {
f_EPTF_Common_user(%definitionId&": No data was concatenated from segment buffer to continuous buffer.");
}
}
}
if(pl_buffer.finSegmentOffset > 0) {
pl_buffer.finSegmentOffset :=
pl_buffer.finSegmentOffset - vl_concatenated;
if(pl_buffer.finSegmentOffset < 0) { pl_buffer.finSegmentOffset := 0; }
}
if(c_EPTF_Common_debugSwitch and tsp_EPTF_TransportIPL2_enableLogging) {
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
var octetstring vl_outData;
f_EPTF_Buffer_get_read_data(pl_buffer.continuousBufferID, vl_outData);
f_EPTF_Common_user(%definitionId&": Continuous buffer: "& log2str(vl_outData));
} else {
f_EPTF_Common_user(%definitionId&": Continuous buffer: "& log2str(pl_buffer.continuousBuffer));
}
f_EPTF_SegmentBuffer_logSegments(%definitionId, pl_buffer);
}
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
pl_size := f_EPTF_Buffer_get_read_len(pl_buffer.continuousBufferID);
} else {
pl_size := lengthof(pl_buffer.continuousBuffer);
}
return vl_concatenated;
} else {
var integer vl_new := f_EPTF_FBQ_getOrCreateFreeSlot(pl_buffer.nonContSegments.queue);
pl_buffer.nonContSegments.segments[vl_new] := {
seqNum := pl_seqNum,
data := pl_payload
}
pl_buffer.nonContSegments.nofBytes :=
pl_buffer.nonContSegments.nofBytes + lengthof(pl_payload);
if (f_EPTF_FBQ_getBusyHeadIdx(vl_iter, pl_buffer.nonContSegments.queue)) {
if(pl_buffer.nonContSegments.segments[vl_iter].seqNum >= pl_seqNum) {
f_EPTF_FBQ_moveFromFreeHeadToBusy(
vl_new,
head,
vl_iter,
vl_iter,
pl_buffer.nonContSegments.queue);
} else {
// skip segments with lower sequence number than the new segment
var integer vl_insertAfter := vl_iter;
while(pl_buffer.nonContSegments.segments[vl_iter].seqNum < pl_seqNum) {
vl_insertAfter := vl_iter;
if(not f_EPTF_FBQ_getFwdBusyItemIdx(vl_iter, pl_buffer.nonContSegments.queue)) {
break;
}
}
// insert new segment after it
var integer vl_beforeIdx := vl_insertAfter;
if(not f_EPTF_FBQ_getFwdBusyItemIdx(vl_beforeIdx, pl_buffer.nonContSegments.queue)) {
f_EPTF_FBQ_moveFromFreeHeadToBusyTail(pl_buffer.nonContSegments.queue);
} else {
f_EPTF_FBQ_moveFromFreeHeadToBusy(
vl_new,
middle,
vl_insertAfter,
vl_beforeIdx,
pl_buffer.nonContSegments.queue);
}
}
} else {
f_EPTF_FBQ_moveFromFreeHeadToBusyTail(pl_buffer.nonContSegments.queue);
}
if(c_EPTF_Common_debugSwitch and tsp_EPTF_TransportIPL2_enableLogging) {
f_EPTF_Common_user(%definitionId&": Stored data with offset "&int2str(pl_seqNum)&" in segment buffer.");
f_EPTF_SegmentBuffer_logSegments(%definitionId, pl_buffer);
}
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
pl_size := f_EPTF_Buffer_get_read_len(pl_buffer.continuousBufferID);
} else {
pl_size := lengthof(pl_buffer.continuousBuffer);
}
return 0;
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_getContinousLen
//
// Purpose:
// Retrieves the length of the continuous buffer
//
// Parameters:
// - pl_buffer - *inout* <EPTF_SegmentBuffer> - the segment buffer
//
// Return Value:
// Returns the length of the continuous buffer
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_getContinousLen(inout EPTF_SegmentBuffer pl_buffer)
return integer
{
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
return f_EPTF_Buffer_get_read_len(pl_buffer.continuousBufferID);
} else {
return lengthof(pl_buffer.continuousBuffer);
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_getNonContinousLen
//
// Purpose:
// Retrieves the length of the non-continuous buffer
//
// Parameters:
// - pl_buffer - *inout* <EPTF_SegmentBuffer> - the segment buffer
//
// Return Value:
// Returns the number of bytes stored in the non-continuous buffer
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_getNonContinousLen(inout EPTF_SegmentBuffer pl_buffer)
return integer
{
return pl_buffer.nonContSegments.nofBytes;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_getBuffer
//
// Purpose:
// Retrieves the content of the continuous buffer
//
// Parameters:
// - pl_buffer - *inout* <EPTF_SegmentBuffer> - the segment buffer
// - pl_content - *out* *octetstring* - the place of the string to be retrieved
// - pl_size - *in* *integer* - Length of the string to be retrieved
// the default -1 means the full available buffer length
//
// Return Value:
// -
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_getBuffer(
inout EPTF_SegmentBuffer pl_buffer,
out octetstring pl_content,
in integer pl_size := -1)
{
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
if (pl_size < 0 or pl_size >= f_EPTF_Buffer_get_read_len(pl_buffer.continuousBufferID))
{
f_EPTF_Buffer_get_read_data(pl_buffer.continuousBufferID, pl_content);
} else {
f_EPTF_Buffer_get_read_data(pl_buffer.continuousBufferID, pl_content);
pl_content := substr(pl_content, 0, pl_size);
}
} else {
if (pl_size < 0 or pl_size >= lengthof(pl_buffer.continuousBuffer))
{
pl_content := pl_buffer.continuousBuffer;
} else {
pl_content := substr(pl_buffer.continuousBuffer, 0, pl_size);
}
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_SegmentBuffer_truncBuffer
//
// Purpose:
// Removes the specified amount from the beginning of the buffer
//
// Parameters:
// - pl_buffer - *inout* <EPTF_SegmentBuffer> - the segment buffer
// - pl_size - *in* *integer* - Length of the string to be deleted.
// The default -1 means the full buffer
//
// Return Value:
// *integer* - the length of the deleted string
//
///////////////////////////////////////////////////////////
public function f_EPTF_SegmentBuffer_truncBuffer(
inout EPTF_SegmentBuffer pl_buffer,
in integer pl_size := -1)
return integer
{
if (tsp_EPTF_SegmentBuffer_useCLLBuffer == true) {
var integer vl_len := f_EPTF_Buffer_get_read_len(pl_buffer.continuousBufferID);
if (pl_size < 0 or pl_size >= vl_len)
{
f_EPTF_Buffer_cut_end(pl_buffer.continuousBufferID);
f_EPTF_Buffer_cut(pl_buffer.continuousBufferID);
} else {
f_EPTF_Buffer_set_pos(pl_buffer.continuousBufferID, pl_size);
f_EPTF_Buffer_cut(pl_buffer.continuousBufferID);
vl_len := pl_size;
}
return vl_len;
} else {
var integer vl_len := lengthof(pl_buffer.continuousBuffer)
if (pl_size < 0 or pl_size >= vl_len)
{
pl_buffer.continuousBuffer := ''O;
} else {
pl_buffer.continuousBuffer := substr(pl_buffer.continuousBuffer, pl_size, vl_len-pl_size);
vl_len := pl_size;
}
return vl_len;
}
}
} // group EPTF_TransportIPL2_SegmentBuffer
group EPTF_TransportIPL2_RouteHandler {
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_initRouting
//
// Purpose:
// To initilize the routing handler
//
// Parameters:
// -
//
// Return Value:
// -
//
// Detailed Comments:
// It initializes the routing table database, host routing table
// is loaded into the database.
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_initRouting()
runs on EPTF_TransportIPL2_CT
{
v_EPTF_TransportIPL2_routeTable := {};
v_EPTF_TransportIPL2_gwArpDB := {};
v_EPTF_TransportIPL2_ipArpDB := {
{}, -1
}
v_EPTF_TransportIPL2_ipArpDB.hashmapId := f_EPTF_oct2int_HashMap_New("ipArpDB");
var EPTF_TransportIPL2_RouteTable vl_table;
f_EPTF_TransportIPL2_getHostRoutingTable(vl_table);
f_EPTF_TransportIPL2_setRouteTable(vl_table);
if(sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList) > 0) {
f_EPTF_TransportIPL2_addRoute(
f_EPTF_TransportIPL2_getIpFromHostName(tsp_EPTF_TransportIPL2_loopbackInterfaceAddress),
c_EPTF_TransportIPL2_NullIpAddress,
f_EPTF_TransportIPL2_getIpFromHostName(tsp_EPTF_TransportIPL2_loopbackInterfaceMask),
tsp_EPTF_TransportIPL2_loopbackInterface);
}
}
///////////////////////////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_getNextHop
//
// Purpose:
// Returns the next hop IP address, interface for the given target IP address.
//
// Parameters:
// pl_ipAddr - *in* <EPTF_TransportIPL2_IpAddress> - the target IP address
// pl_nextHopIpAddr - *out* <EPTF_TransportIPL2_IpAddress> - the next-hop IP address
// pl_nextHopIface - *out charstring* - the interface name of the next hop
// pl_isGw - *out boolean* - true if the next hop IP is a gateway, false if not
// false value means direct access.
//
// Return Value:
// boolean - true if next hop found, false if there is no route to the target IP
//
// Errors:
// -
//
// Detailed Comments:
// -
//
///////////////////////////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_getNextHop(
// in EPTF_TransportIPL2_RouteTable pl_routeTable,
in EPTF_TransportIPL2_IpAddress pl_ipAddr,
out EPTF_TransportIPL2_IpAddress pl_nextHopIpAddr,
out charstring pl_nextHopIface,
out boolean pl_isGw)
runs on EPTF_TransportIPL2_CT
return boolean {
var integer vl_nextHopIfaceIdx;
return f_EPTF_TransportIPL2_getNextHopPrivate(pl_ipAddr, pl_nextHopIpAddr, pl_nextHopIface, vl_nextHopIfaceIdx, pl_isGw);
}
private function f_EPTF_TransportIPL2_getNextHopPrivate(
// in EPTF_TransportIPL2_RouteTable pl_routeTable,
in EPTF_TransportIPL2_IpAddress pl_ipAddr,
out EPTF_TransportIPL2_IpAddress pl_nextHopIpAddr,
out charstring pl_nextHopIface,
out integer pl_nextHopIfaceIdx,
out boolean pl_isGw)
runs on EPTF_TransportIPL2_CT
return boolean {
pl_nextHopIpAddr := c_EPTF_TransportIPL2_NullIpAddress;
pl_nextHopIface := "";
pl_isGw := false;
for (var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_routeTable); i:=i+1) {
if (v_EPTF_TransportIPL2_routeTable[i].destination == pl_ipAddr and4b v_EPTF_TransportIPL2_routeTable[i].genmask) {
pl_nextHopIface := v_EPTF_TransportIPL2_routeTable[i].iface;
pl_nextHopIfaceIdx := v_EPTF_TransportIPL2_routeTableToEthIF[i];
if (v_EPTF_TransportIPL2_routeTable[i].gateway != c_EPTF_TransportIPL2_NullIpAddress) {
pl_nextHopIpAddr := v_EPTF_TransportIPL2_routeTable[i].gateway;
pl_isGw := true;
} else {
pl_nextHopIpAddr := pl_ipAddr;
}
return true; // for
}
}
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_normalizeRoutingTable
//
// Purpose:
// It normalizes the routing table based on netmask.
//
// Parameters:
// -
//
// Return Value:
// -
//
// Errors:
// -
//
// Detailed Comments:
// See more detailes at: http://www.softpanorama.org/Algorithms/Sorting/bubblesort.shtml.
//
///////////////////////////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_normalizeRoutingTable()
runs on EPTF_TransportIPL2_CT
{
var integer n := sizeof(v_EPTF_TransportIPL2_routeTable);
var integer k;
var integer bound := n-1;
var EPTF_TransportIPL2_RouteTableEntry t;
var integer last_swap;
while (bound!=0) {
last_swap := 0;
for ( k:=0; k<bound; k:=k+1 ) {
t := v_EPTF_TransportIPL2_routeTable[k]; // t is a minimum of A[0]..A[k]
if ( oct2int(t.genmask) < oct2int(v_EPTF_TransportIPL2_routeTable[k+1].genmask) ) {
v_EPTF_TransportIPL2_routeTable[k] := v_EPTF_TransportIPL2_routeTable[k+1]; v_EPTF_TransportIPL2_routeTable[k+1] := t; //swap
last_swap := k; // mark the last swap position
}//if
}//for
bound:=last_swap; // elements after bound already sorted
}//while
f_EPTF_TransportIPL2_updateRouteTableToEthIF();
}
private function f_EPTF_TransportIPL2_updateRouteTableToEthIF()
runs on EPTF_TransportIPL2_CT
{
v_EPTF_TransportIPL2_routeTableToEthIF := {};
for(var integer i:=0; i<sizeof(v_EPTF_TransportIPL2_routeTable); i:=i+1) {
v_EPTF_TransportIPL2_routeTableToEthIF[i] := -1;
for(var integer j:=0; j<sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList); j:=j+1) {
if(v_EPTF_TransportIPL2_routeTable[i].iface == v_EPTF_TransportIPL2_ethernetInterfaceList[j].name) {
v_EPTF_TransportIPL2_routeTableToEthIF[i] := j;
break;
}
}
}
if(c_EPTF_Common_debugSwitch and f_EPTF_TransportIPL2_debugEnabled()) {
f_EPTF_TransportIPL2_debug(%definitionId&": ethernet interface DB: " & log2str(v_EPTF_TransportIPL2_ethernetInterfaceList));
f_EPTF_TransportIPL2_debug(%definitionId&": route table: " & log2str(v_EPTF_TransportIPL2_routeTable));
f_EPTF_TransportIPL2_debug(%definitionId&": route table -> interface association list: " & log2str(v_EPTF_TransportIPL2_routeTableToEthIF));
}
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_getHostRoutingTable
//
// Purpose:
// To retrieve the host system's routing table.
//
// Parameters:
// - pl_table - *out* <EPTF_TransportIPL2_RouteTable> - the routing table
//
// Return Value:
// -
//
// Detailed Comments:
// It retrieves the host system's routing table and saves it into pl_table.
//
///////////////////////////////////////////////////////////
public external function f_EPTF_TransportIPL2_getHostRoutingTable(
out EPTF_TransportIPL2_RouteTable pl_table);
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_getRouteTable
//
// Purpose:
// To retrieve the current routing table from the database.
//
// Parameters:
// -
//
// Return Value:
// <EPTF_TransportIPL2_RouteTable> - the routing table
//
// Detailed Comments:
// It retrieves the current routing table and returns it.
//
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_getRouteTable()
runs on EPTF_TransportIPL2_CT
return EPTF_TransportIPL2_RouteTable
{
return v_EPTF_TransportIPL2_routeTable;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_setRouteTable
//
// Purpose:
// To set the current routing table into the database.
//
// Parameters:
// - pl_routeTable - *in* <EPTF_TransportIPL2_RouteTable> - the routing
// table
//
// Return Value:
// <EPTF_TransportIPL2_RouteTable> - the routing table
//
// Detailed Comments:
// It loads the specified routing table into the database and normalizes it
//
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_setRouteTable(
in EPTF_TransportIPL2_RouteTable pl_routeTable
)
runs on EPTF_TransportIPL2_CT
{
v_EPTF_TransportIPL2_routeTable := pl_routeTable;
f_EPTF_TransportIPL2_normalizeRoutingTable();
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_addRoute
//
// Purpose:
// To add a route to the routing table.
//
// Parameters:
// - pl_destination - *in* <EPTF_TransportIPL2_IpAddress> - the destination
// network
// - pl_gateway - *in* <EPTF_TransportIPL2_IpAddress> - the gateway address
// - pl_genmask - *in* <EPTF_TransportIPL2_IpAddress> - the subnet mask of
// destination network
// - pl_iface - *in* *charstring* - the destination interface
// (currently not used)
//
// Detailed Comments:
// It adds the specified route to the routing table and normalizes it
//
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_addRoute(
in EPTF_TransportIPL2_IpAddress pl_destination,
in EPTF_TransportIPL2_IpAddress pl_gateway,
in EPTF_TransportIPL2_IpAddress pl_genmask,
in charstring pl_iface
)
runs on EPTF_TransportIPL2_CT
{
var integer vl_idx := sizeof(v_EPTF_TransportIPL2_routeTable);
v_EPTF_TransportIPL2_routeTable[vl_idx] := {
pl_destination,
pl_gateway,
pl_genmask,
pl_iface
}
f_EPTF_TransportIPL2_normalizeRoutingTable();
}
} // group EPTF_TransportIPL2_RouteHandler
group EPTF_TransportIPL2_ARP {
private function f_EPTF_TransportIPL2_createARPRequest(
in EPTF_TransportIPL2_IpAddress pl_targetIPaddr,
in EPTF_TransportIPL2_IpAddress pl_senderIPaddr,
in EPTF_TransportIPL2_MACAddress pl_senderMACaddr )
return octetstring
{
return cg_EPTF_TransportIPL2_ARP_prefix&'0001'O&pl_senderMACaddr&
pl_senderIPaddr&cg_EPTF_TransportIPL2_NullMACAddress&pl_targetIPaddr;
}
private function f_EPTF_TransportIPL2_createARPReply(
in EPTF_TransportIPL2_IpAddress pl_targetIPaddr,
in EPTF_TransportIPL2_MACAddress pl_targetMACaddr,
in EPTF_TransportIPL2_IpAddress pl_senderIPaddr,
in EPTF_TransportIPL2_MACAddress pl_senderMACaddr )
return octetstring
{
return cg_EPTF_TransportIPL2_ARP_prefix&'0002'O&pl_senderMACaddr&
pl_senderIPaddr&pl_targetMACaddr&pl_targetIPaddr;
}
private function f_EPTF_TransportIPL2_decodeARP(
in octetstring pl_pdu,
inout ARP_packet pl_arpPacket )
{
var integer vl_pos := lengthof(cg_EPTF_TransportIPL2_ARP_prefix);
pl_arpPacket.arpType := substr(pl_pdu, vl_pos, 2);
vl_pos := vl_pos + 2;
pl_arpPacket.senderMACAddr := substr(pl_pdu, vl_pos, 6);
vl_pos := vl_pos + 6;
pl_arpPacket.senderIPAddr := substr(pl_pdu, vl_pos, 4);
vl_pos := vl_pos + 4;
pl_arpPacket.targetMACAddr := substr(pl_pdu, vl_pos, 6);
vl_pos := vl_pos + 6;
pl_arpPacket.targetIPAddr := substr(pl_pdu, vl_pos, 4);
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_ARP_getMac
//
// Purpose:
// To determine the MAC address for the specified IP address.
//
// Parameters:
// - pl_sourceIpAddr - *in* <EPTF_TransportIPL2_IpAddress> - the source
// IP address
// - pl_sourceMACAddr - *in* <EPTF_TransportIPL2_MACAddress> - the source
// MAC address
// - pl_targetIpAddr - *in* <EPTF_TransportIPL2_IpAddress> - the IP address
// for which the next hop MAC address should be determined
//
// Return Value:
// <EPTF_TransportIPL2_MACAddress> - the MAC address of the next hop
// for the target IP address.
//
// Detailed Comments:
// The returned MAC address belongs to the next hop of the specified IP
// address.
//
///////////////////////////////////////////////////////////
public function f_EPTF_TransportIPL2_ARP_getMac(
in EPTF_TransportIPL2_IpAddress pl_sourceIpAddr,
in EPTF_TransportIPL2_MACAddress pl_sourceMACAddr,
in EPTF_TransportIPL2_IpAddress pl_targetIpAddr)
runs on EPTF_TransportIPL2_CT
return EPTF_TransportIPL2_MACAddress
{
var integer vl_nextHopIfaceIdx;
var EPTF_TransportIPL2_MACAddress vl_nextHopMacAddress;
if(f_EPTF_TransportIPL2_ARP_getMacPrivate(pl_sourceIpAddr, pl_sourceMACAddr, pl_targetIpAddr, vl_nextHopIfaceIdx, vl_nextHopMacAddress)) {
return vl_nextHopMacAddress;
} else {
return cg_EPTF_TransportIPL2_NullMACAddress;
}
}
private function f_EPTF_TransportIPL2_ARP_getMacPrivate(
in EPTF_TransportIPL2_IpAddress pl_sourceIpAddr,
in EPTF_TransportIPL2_MACAddress pl_sourceMACAddr,
in EPTF_TransportIPL2_IpAddress pl_targetIpAddr,
out integer pl_nextHopIfaceIdx,
out EPTF_TransportIPL2_MACAddress pl_nextHopMacAddress)
runs on EPTF_TransportIPL2_CT
return boolean // true on success
{
var boolean vl_isGw;
var EPTF_TransportIPL2_IpAddress vl_nextHopIpAddr;
var charstring vl_nextHopIface;
var EPTF_TransportIPL2_MACAddress vl_srcMacAddr := pl_sourceMACAddr;
if (f_EPTF_TransportIPL2_hasIp(pl_targetIpAddr)) {
pl_nextHopMacAddress := vl_srcMacAddr; // note: this can be the null MAC address in case of the loopback IF, thus the boolean return type!
pl_nextHopIfaceIdx := v_EPTF_TransportIPL2_loopbackIF;
return true;
}
if (not f_EPTF_TransportIPL2_getNextHopPrivate(pl_targetIpAddr, vl_nextHopIpAddr, vl_nextHopIface, pl_nextHopIfaceIdx, vl_isGw)) {
f_EPTF_TransportIPL2_warning(%definitionId&": No route to host: "&f_EPTF_TransportIPL2_ip2str(pl_targetIpAddr));
pl_nextHopMacAddress := cg_EPTF_TransportIPL2_NullMACAddress;
return false;
}
if(sizeof(v_EPTF_TransportIPL2_ethernetInterfaceList) > 0) { // multiple interface mode
if(pl_nextHopIfaceIdx < 0) {
f_EPTF_TransportIPL2_warning(%definitionId&": multiple interface mode, interface was not opened: "&vl_nextHopIface);
pl_nextHopMacAddress := cg_EPTF_TransportIPL2_NullMACAddress;
return false;
}
vl_srcMacAddr := v_EPTF_TransportIPL2_ethernetInterfaceList[pl_nextHopIfaceIdx].macAddress;
}
if (vl_isGw) {
pl_nextHopMacAddress := f_EPTF_TransportIPL2_ARP_getMacForGw(pl_nextHopIfaceIdx, pl_sourceIpAddr, vl_srcMacAddr, vl_nextHopIpAddr);
} else {
pl_nextHopMacAddress := f_EPTF_TransportIPL2_ARP_getMacForIp(pl_nextHopIfaceIdx, pl_sourceIpAddr, vl_srcMacAddr, vl_nextHopIpAddr);
}
if(cg_EPTF_TransportIPL2_NullMACAddress == pl_nextHopMacAddress) {
return false;
} else {
return true;
}
}
// Determines the MAC address of the pl_gwIpAddr
private function f_EPTF_TransportIPL2_ARP_getMacForGw(
in integer pl_interfaceId,
in EPTF_TransportIPL2_IpAddress pl_sourceIpAddr,
in EPTF_TransportIPL2_MACAddress pl_sourceMACAddr,
in EPTF_TransportIPL2_IpAddress pl_gwIpAddr)
runs on EPTF_TransportIPL2_CT
return EPTF_TransportIPL2_MACAddress
{
var boolean vl_needInsert := true;
var integer vl_idx := -1;
for ( var integer i := 0; i < sizeof(v_EPTF_TransportIPL2_gwArpDB) ; i := i+1 ) {
if (v_EPTF_TransportIPL2_gwArpDB[i].gateway != pl_gwIpAddr) {
continue;
}
vl_needInsert := false;
vl_idx := i;
if (v_EPTF_TransportIPL2_gwArpDB[i].macaddr != cg_EPTF_TransportIPL2_NullMACAddress) {
return v_EPTF_TransportIPL2_gwArpDB[i].macaddr;
}
break;
}
if (vl_idx == -1) {
vl_idx := sizeof(v_EPTF_TransportIPL2_gwArpDB);
v_EPTF_TransportIPL2_gwArpDB[vl_idx].gateway := pl_gwIpAddr;
v_EPTF_TransportIPL2_gwArpDB[vl_idx].macaddr := cg_EPTF_TransportIPL2_NullMACAddress;
v_EPTF_TransportIPL2_gwArpDB[vl_idx].semaphoreIdx := -1;
}
// if the mac was already requested, but response is not arrived yet, then
// wait until response comes (or timeout):
if (v_EPTF_TransportIPL2_gwArpDB[vl_idx].semaphoreIdx != -1) {
timer t_wait := 0.0;
t_wait.start;
alt {
[v_EPTF_TransportIPL2_gwArpDB[vl_idx].semaphoreIdx == -1] t_wait.timeout {
return v_EPTF_TransportIPL2_gwArpDB[vl_idx].macaddr;
}
}
}
f_EPTF_TransportIPL2_ARP_sendMACRequestForIp(pl_interfaceId, pl_sourceIpAddr, pl_sourceMACAddr, pl_gwIpAddr); // TODO
v_EPTF_TransportIPL2_gwArpDB[vl_idx].semaphoreIdx := f_EPTF_Semaphore_new();
var boolean vl_timeout := f_EPTF_Semaphore_waitForUnlock(v_EPTF_TransportIPL2_gwArpDB[vl_idx].semaphoreIdx, tsp_EPTF_TransportIPL2_arpMaxWaitTime);
v_EPTF_TransportIPL2_gwArpDB[vl_idx].semaphoreIdx := -1;
if (vl_timeout) {
return cg_EPTF_TransportIPL2_NullMACAddress;
}
return v_EPTF_TransportIPL2_gwArpDB[vl_idx].macaddr;
}
// Determines the MAC address of the pl_gwIpAddr
private function f_EPTF_TransportIPL2_ARP_getMacForIp(
in integer pl_interfaceId,
in EPTF_TransportIPL2_IpAddress pl_sourceIpAddr,
in EPTF_TransportIPL2_MACAddress pl_sourceMACAddr,
in EPTF_TransportIPL2_IpAddress pl_targetIpAddr)
runs on EPTF_TransportIPL2_CT
return EPTF_TransportIPL2_MACAddress
{
var integer vl_idx;
if (f_EPTF_oct2int_HashMap_Find(v_EPTF_TransportIPL2_ipArpDB.hashmapId, pl_targetIpAddr, vl_idx)) {
if ( v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].macaddr != cg_EPTF_TransportIPL2_NullMACAddress) {
return v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].macaddr;
}
} else {
vl_idx := -1;
}
if (vl_idx == -1) {
vl_idx := sizeof(v_EPTF_TransportIPL2_ipArpDB.data);
f_EPTF_oct2int_HashMap_Insert(v_EPTF_TransportIPL2_ipArpDB.hashmapId, pl_targetIpAddr, vl_idx);
v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].macaddr := cg_EPTF_TransportIPL2_NullMACAddress;
v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx := -1;
}
// if the mac was already requested, but response is not arrived yet, then
// wait until response comes (or timeout):
if (v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx != -1) {
timer t_wait := 0.0;
t_wait.start;
alt {
[v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx == -1] t_wait.timeout {
return v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].macaddr;
}
}
}
f_EPTF_TransportIPL2_ARP_sendMACRequestForIp(pl_interfaceId, pl_sourceIpAddr, pl_sourceMACAddr, pl_targetIpAddr); // TODO
v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx := f_EPTF_Semaphore_new();
var boolean vl_timeout := f_EPTF_Semaphore_waitForUnlock(v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx, tsp_EPTF_TransportIPL2_arpMaxWaitTime);
v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx := -1;
if (vl_timeout) {
return cg_EPTF_TransportIPL2_NullMACAddress;
}
return v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].macaddr;
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_ARP_sendMACRequestForIp
//
// Purpose:
// To send an ARP request for the specified IP address.
//
// Parameters:
// - pl_sourceIpAddr - *in* <EPTF_TransportIPL2_IpAddress> - the source
// IP address
// - pl_sourceMACAddr - *in* <EPTF_TransportIPL2_MACAddress> - the source
// MAC address
// - pl_targetIpAddr - *in* <EPTF_TransportIPL2_IpAddress> - the IP address
// for which the MAC address should be determined
//
// Detailed Comments:
// The ARP reply handled by the <as_EPTF_TransportIPL2_defaultReceive>
// altstep.
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_ARP_sendMACRequestForIp(
in integer pl_interfaceId,
in EPTF_TransportIPL2_IpAddress pl_sourceIpAddr,
in EPTF_TransportIPL2_MACAddress pl_sourceMACAddr,
in EPTF_TransportIPL2_IpAddress pl_targetIpAddr)
runs on EPTF_TransportIPL2_CT
{
var octetstring vl_data := f_EPTF_TransportIPL2_createARPRequest(
pl_targetIpAddr, pl_sourceIpAddr, pl_sourceMACAddr);
f_EPTF_TransportIPL2_sendOverL2(
pl_interfaceId,
pl_eth_dst_addr := cg_EPTF_TransportIPL2_BroadcastMACAddress,
//pl_eth_src_addr := pl_sourceMACAddr,
pl_type_field := int2oct(c_ip_gre_proto_arp, 2),
pl_payload := vl_data);
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_ARP_sendMACReplyForIp
//
// Purpose:
// To send an ARP reply.
//
// Parameters:
// - pl_interfaceId - *in* *integer* - ethernet interface ID
// - pl_sourceIpAddr - *in* <EPTF_TransportIPL2_IpAddress> - the source
// IP address
// - pl_sourceMACAddr - *in* <EPTF_TransportIPL2_MACAddress> - the source
// MAC address
// - pl_targetIpAddr - *in* <EPTF_TransportIPL2_IpAddress> - the target
// IP address
// - pl_targetMACAddr - *in* <EPTF_TransportIPL2_MACAddress> - the target
// MAC address
//
// Detailed Comments:
// -
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_ARP_sendMACReplyForIp(
in integer pl_interfaceId,
in EPTF_TransportIPL2_IpAddress pl_sourceIpAddr,
in EPTF_TransportIPL2_MACAddress pl_sourceMACAddr,
in EPTF_TransportIPL2_IpAddress pl_targetIpAddr,
in EPTF_TransportIPL2_MACAddress pl_targetMACAddr)
runs on EPTF_TransportIPL2_CT
{
var octetstring vl_arpReply := f_EPTF_TransportIPL2_createARPReply(
pl_targetIpAddr, //target
pl_targetMACAddr,
pl_sourceIpAddr, // sender
pl_sourceMACAddr);
f_EPTF_TransportIPL2_sendOverL2(
pl_interfaceId,
pl_targetMACAddr,
//pl_sourceMACAddr,
int2oct(c_ip_gre_proto_arp, 2),
vl_arpReply);
}
private function f_EPTF_TransportIPL2_addMacToGwArpDB(
in ARP_packet pl_arpPacket )
runs on EPTF_TransportIPL2_CT
return boolean
{
for ( var integer i := 0; i < sizeof(v_EPTF_TransportIPL2_gwArpDB) ; i := i+1 ) {
if (v_EPTF_TransportIPL2_gwArpDB[i].gateway == pl_arpPacket.senderIPAddr) {
v_EPTF_TransportIPL2_gwArpDB[i].macaddr := pl_arpPacket.senderMACAddr;
if (v_EPTF_TransportIPL2_gwArpDB[i].semaphoreIdx != -1) {
f_EPTF_Semaphore_unlock(v_EPTF_TransportIPL2_gwArpDB[i].semaphoreIdx);
}
return true;
}
}
return false;
}
private function f_EPTF_TransportIPL2_addMacToIpArpDB(
in ARP_packet pl_arpPacket )
runs on EPTF_TransportIPL2_CT
return boolean
{
var integer vl_idx;
if (f_EPTF_oct2int_HashMap_Find(v_EPTF_TransportIPL2_ipArpDB.hashmapId, pl_arpPacket.senderIPAddr, vl_idx)) {
v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].macaddr := pl_arpPacket.senderMACAddr;
if (v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx != -1) {
f_EPTF_Semaphore_unlock(v_EPTF_TransportIPL2_ipArpDB.data[vl_idx].semaphoreIdx);
}
return true;
}
return false;
}
} //group ARP
///////////////////////////////////////////////////////////////////////////////
// Group: EPTF_TransportIPL2_Logging
//
// Purpose:
// The functions of the EPTF CommPort IPL2 Logging
//
///////////////////////////////////////////////////////////////////////////////
group EPTF_TransportIPL2_Logging
{
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_error
//
// Purpose:
// Function to log an error from StatMeasure feature.
//
// Parameters:
// - pl_message - *in* *charstring* - the message to log
//
// Return Value:
// -
//
// Errors & assertions:
// -
//
// Detailed Comments:
// -
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_error(
in charstring pl_message)
runs on EPTF_TransportIPL2_CT
{
f_EPTF_Logging_error(true, tsp_EPTF_TransportIPL2_loggingComponentMask&": "&pl_message);
f_EPTF_Base_stopAll();
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_warning
//
// Purpose:
// Function to log a warning from StatMeasure feature.
//
// Parameters:
// - pl_message - *in* *charstring* - the message to log
//
// Return Value:
// -
//
// Errors & assertions:
// -
//
// Detailed Comments:
// -
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_warning(
in @lazy charstring pl_message)
runs on EPTF_TransportIPL2_CT
{
f_EPTF_Logging_warningV2(pl_message,
v_EPTF_TransportIPL2_loggingMaskId,
{c_EPTF_TransportIPL2_loggingClassIdx_Warning});
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_debug
//
// Purpose:
// Function to log a debug message from StatMeasure feature.
//
// Parameters:
// - pl_message - *in* *charstring* - the message to log
//
// Return Value:
// -
//
// Errors & assertions:
// -
//
// Detailed Comments:
// -
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_debug(
in @lazy charstring pl_message)
runs on EPTF_TransportIPL2_CT
{
f_EPTF_Logging_debugV2(pl_message,
v_EPTF_TransportIPL2_loggingMaskId,
{c_EPTF_TransportIPL2_loggingClassIdx_Debug});
}
///////////////////////////////////////////////////////////
// Function: f_EPTF_TransportIPL2_debugEnabled
//
// Purpose:
// Function to check if debug is enabled for StatMeasure
//
// Parameters:
// -
//
// Return Value:
// *boolean* - true if debug enalbed
//
// Errors & assertions:
// -
//
// Detailed Comments:
// -
//
///////////////////////////////////////////////////////////
private function f_EPTF_TransportIPL2_debugEnabled()
runs on EPTF_TransportIPL2_CT
return boolean
{
return f_EPTF_Logging_isEnabled(
v_EPTF_TransportIPL2_loggingMaskId,
c_EPTF_TransportIPL2_loggingClassIdx_Debug);
}
} // group Logging
}