| /////////////////////////////////////////////////////////////////////////////// |
| // // |
| // 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 |
| |
| } |