networkprotocols/tcpipv4v6prt/src/tcp_sap.cpp
changeset 0 af10295192d8
child 8 e9cc36e353d4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/tcp_sap.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,4585 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// tcp_sap.cpp - TCP service access point
+// TCP Service Access Point and most of the protocol logic.
+//
+
+
+
+/**
+ @file tcp_sap.cpp
+*/
+
+#include "tcp.h"
+#include <in6_dstcache.h>
+#include <in6_opt.h>
+#include <in6_if.h>
+#include <in6_dstcache_internal.h>
+#include <nifman_internal.h>
+
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+#include <in_sock.h>
+#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+
+// speed optimisations
+#ifdef __ARMCC__
+#pragma push
+#pragma arm
+#endif
+
+#define SIGNED_UNSIGNED_FIX
+
+// Copied from ip6.cpp. Should move to some common definition file?
+static const TLitC8<sizeof(TInt)> KInetOptionDisable = {sizeof(TInt), {0}};
+
+#define SYMBIAN_NETWORKING_UPS
+
+//
+//
+// TCP state diagram from RFC793
+//
+//
+//                              +---------+ ---------\      active OPEN
+//                              |  CLOSED |            \    -----------
+//                              +---------+<---------\   \   create TCB
+//                                |     ^              \   \  snd SYN
+//                   passive OPEN |     |   CLOSE        \   \.
+//                   ------------ |     | ----------       \   \.
+//                    create TCB  |     | delete TCB         \   \.
+//                                V     |                      \   \.
+//                              +---------+            CLOSE    |    \.
+//                              |  LISTEN |          ---------- |     |
+//                              +---------+          delete TCB |     |
+//                   rcv SYN      |     |     SEND              |     |
+//                  -----------   |     |    -------            |     V
+// +---------+      snd SYN,ACK  /       \   snd SYN          +---------+
+// |         |<-----------------           ------------------>|         |
+// |   SYN   |                    rcv SYN                     |   SYN   |
+// |   RCVD  |<-----------------------------------------------|   SENT  |
+// |         |                    snd ACK                     |         |
+// |         |------------------           -------------------|         |
+// +---------+   rcv ACK of SYN  \       /  rcv SYN,ACK       +---------+
+//   |           --------------   |     |   -----------
+//   |                  x         |     |     snd ACK
+//   |                            V     V
+//   |  CLOSE                   +---------+
+//   | -------                  |  ESTAB  |
+//   | snd FIN                  +---------+
+//   |                   CLOSE    |     |    rcv FIN
+//   V                  -------   |     |    -------
+// +---------+          snd FIN  /       \   snd ACK          +---------+
+// |  FIN    |<-----------------           ------------------>|  CLOSE  |
+// | WAIT-1  |------------------                              |   WAIT  |
+// +---------+          rcv FIN  \                            +---------+
+//   | rcv ACK of FIN   -------   |                            CLOSE  |
+//   | --------------   snd ACK   |                           ------- |
+//   V        x                   V                           snd FIN V
+// +---------+                  +---------+                   +---------+
+// |FINWAIT-2|                  | CLOSING |                   | LAST-ACK|
+// +---------+                  +---------+                   +---------+
+//   |                rcv ACK of FIN |                 rcv ACK of FIN |
+//   |  rcv FIN       -------------- |    Timeout=2MSL -------------- |
+//   |  -------              x       V    ------------        x       V
+//    \ snd ACK                 +---------+delete TCB         +---------+
+//     ------------------------>|TIME WAIT|------------------>| CLOSED  |
+//                              +---------+                   +---------+
+//
+//  LISTEN - represents waiting for a connection request from any remote
+//  TCP and port.
+//
+//  SYN-SENT - represents waiting for a matching connection request
+//  after having sent a connection request.
+//
+//  SYN-RECEIVED - represents waiting for a confirming connection
+//  request acknowledgment after having both received and sent a
+//  connection request.
+//
+//  ESTABLISHED - represents an open connection, data received can be
+//  delivered to the user.  The normal state for the data transfer phase
+//  of the connection.
+//
+//  FIN-WAIT-1 - represents waiting for a connection termination request
+//  from the remote TCP, or an acknowledgment of the connection
+//  termination request previously sent.
+//
+//  FIN-WAIT-2 - represents waiting for a connection termination request
+//  from the remote TCP.
+//
+//  CLOSE-WAIT - represents waiting for a connection termination request
+//  from the local user.
+//
+//  CLOSING - represents waiting for a connection termination request
+//  acknowledgment from the remote TCP.
+//
+//  LAST-ACK - represents waiting for an acknowledgment of the
+//  connection termination request previously sent to the remote TCP
+//  (which includes an acknowledgment of its connection termination request).
+//
+//  TIME-WAIT - represents waiting for enough time to pass to be sure
+//  the remote TCP received the acknowledgment of its connection
+//  termination request.
+//
+//  CLOSED - represents no connection state at all.
+//
+//  CONNECT - represents waiting for a network path to become ready after
+//  the user has issued an active OPEN request. The request waits in this
+//  state until all the necessary negotiations have been accomplished,
+//  including the establishment of security and mobility bindings. CONNECT is
+//  specific to this TCP implementation and is therefore not visible in the
+//  state chart above. It would be located between CLOSED and SYN-SENT.
+//
+
+
+#ifdef _LOG
+const TText *CProviderTCP6::TcpState(TUint aState)
+	{
+	TInt i;
+	static const TText* const tcpStates[] = {
+		_S("CONSTRUCTING"),
+			_S("INITIAL"),
+			_S("LISTEN"),
+			_S("SYN-SENT"),
+			_S("SYN-RECEIVED"),
+			_S("ESTABLISHED"),
+			_S("FIN-WAIT-1"),
+			_S("FIN-WAIT-2"),
+			_S("CLOSE-WAIT"),
+			_S("CLOSING"),
+			_S("LAST-ACK"),
+			_S("TIME-WAIT"),
+			_S("CLOSED"),
+			_S("CONNECT"),
+			_S("INVALID"),
+			NULL
+			};
+
+	if (aState == ~0UL)
+		aState = iState;
+	for (i = 0; aState && tcpStates[i+1]; aState >>= 1)
+		i++;
+	return tcpStates[i];
+	}
+#endif
+
+CProviderTCP6::CProviderTCP6(CProtocolInet6Base* aProtocol)
+	: CProviderInet6Transport(aProtocol)
+	{
+	__DECLARE_NAME(_S("CProviderTCP6"));
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW	
+	iWindowSetByUser = EFalse;
+#endif	
+	}
+
+CProviderTCP6::~CProviderTCP6()
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] being deleted"), (TInt)this));
+	Stop();
+	FreeQueues();
+
+	delete iTransmitter;
+	delete iDelayAckTimer;
+	delete iRetransTimer;
+	delete iLingerTimer;
+
+	// Delete server socket state
+	if (iParent)
+		{
+		// Notify parent.
+		iParent->SetChildDeleted(ETrue);
+		iParent->DetachChild(this);
+		}
+	else if (iListenQueue)
+		{
+		//
+		// Delete all pending child sockets. The child socket destructor will
+		// call DetachChild(), removing itself from the socket queue. If the
+		// socket has already been registered with the socket server, we will
+		// simply detach it here.
+		//
+		while (iConnectCount)
+			{
+			ASSERT(iListenQueue[0]->iParent == this);
+			if (iListenQueue[0]->iSockFlags.iAttached)
+				DetachChild(iListenQueue[0]);
+			else
+			    {
+			    if (iListenQueue[0]->InState(ETcpSynReceived))
+			    	{
+			    	iListenQueue[0]->ClearSYNSettings();
+				    iListenQueue[0]->iSockFlags.iSendClose = ETrue;
+				    iListenQueue[0]->iSockFlags.iRecvClose = ETrue;
+				    iListenQueue[0]->SendSegments();
+				    DetachChild(iListenQueue[0]);
+			    	}
+			   	else
+					delete iListenQueue[0];
+			    }
+			}
+		delete[] iListenQueue;
+		}
+	}
+
+//
+// Initialize a SAP with default values
+//
+void CProviderTCP6::InitL()
+	{
+	TCallBack sender(SenderCallBack, this);
+	TCallBack receiver(ReceiverCallBack, this);
+	TCallBack delack(DelayAckCallBack, this);
+	TCallBack transmitter(TransmitterCallBack, this);
+	TCallBack retransmitter(RetransmitterCallBack, this);
+	TCallBack linger(LingerTimerCallBack, this);
+
+	CProviderInet6Base::InitL();
+	iFlow.SetProtocol(KProtocolInetTcp);
+	iFlow.SetNotify(this);
+
+	iSendQ.InitL(transmitter, 13);
+	iRecvQ.InitL(receiver, 12);
+
+	iTransmitter = new CAsyncCallBack(sender, KInet6DefaultPriority);
+	iDelayAckTimer = new CTcpTimer(delack);
+	iRetransTimer = new CTcpTimer(retransmitter);
+	iLingerTimer = new CTcpTimer(linger);
+	if (!iTransmitter || !iDelayAckTimer || !iRetransTimer || !iLingerTimer)
+		User::Leave(KErrNoMemory);
+
+	iDelayAckTimer->InitL();
+	iRetransTimer->InitL();
+	iLingerTimer->InitL();
+	iSockInBufSize        = Protocol()->RecvBuf();
+	iSockOutBufSize       = Protocol()->SendBuf();
+	iSsthresh		= KMaxTInt32;
+	iRTO			= Protocol()->InitialRTO();
+	ClearRTT();
+	iMSS                  = Protocol()->MSS();
+	iSMSS			= KTcpStandardMSS;
+	iRMSS			= KTcpStandardMSS;
+	iLinger		= -1;  // linger disabled
+	iFlags.iSackOk	    = Protocol()->Sack();
+	iFlags.iUseTimeStamps	    = Protocol()->TimeStamps();
+	iFlags.iEcn		    = (Protocol()->Ecn() != 0);
+
+	// Report ICMP errors to application
+	iSockFlags.iReportIcmp    = ETrue;
+
+	if (iFlags.iUseTimeStamps)
+		iOptions.SetTimeStamps(0, 0);
+
+	if (iFlags.iSackOk)
+		iOptions.SetSackOk();
+
+	iOptions.SetAlignOpt(Protocol()->AlignOpt());
+	iStartTime.UniversalTime();
+
+	iState = ETcpInitial;
+	
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+    iHiddenFreeWindow 	= 0;
+    iNewTcpWindow    	= 0;
+    iTcpMaxRecvWin       = Protocol()->RecvMaxWnd();
+#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+
+	}
+
+
+void CProviderTCP6::ReadDestinationCache()
+	{
+	if (!Protocol()->DstCache())  // Dest. cache is not enabled as ini parameter
+		{
+		return;
+		}
+
+	const TInetAddr& dstaddr = iFlow.FlowContext()->RemoteAddr();
+	if (dstaddr.IsUnspecified())
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] ReadDestinationCache() : No destination address"), (TInt)this));
+		return;
+		}
+
+	TInt err = KErrNone;
+	MDestinationCache *dcache = NULL;
+	TRAP(err, dcache = IMPORT_API_L(Protocol()->Interfacer(), MDestinationCache));
+	if (err != KErrNone || dcache == NULL)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] ReadDestinationCache() : DstCache not available"), (TInt)this));
+		return;
+		}
+
+	const TCacheInfo *cinfo = dcache->Find(dstaddr);
+	if (!cinfo)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] ReadDestinationCache() : No match for address in cache"), (TInt)this));
+		return;
+		}
+
+	if (cinfo->iMetrics[TCacheInfo::ESsThresh])
+		{
+		iSsthresh = cinfo->iMetrics[TCacheInfo::ESsThresh];
+		}
+	if (cinfo->iMetrics[TCacheInfo::ESRtt])
+		{
+		iSRTT = cinfo->iMetrics[TCacheInfo::ESRtt];
+		}
+	if (cinfo->iMetrics[TCacheInfo::ERto])
+		{
+		iRTO = cinfo->iMetrics[TCacheInfo::ERto];
+		}
+
+	LOG(Log::Printf(_L(
+		"\ttcp SAP[%u] ReadDestinationCache() : Matching DstCache entry found [0x%08x] - ssthresh: %d"),
+		(TInt)this, cinfo, iSsthresh));
+	}
+
+void CProviderTCP6::StoreDestinationCache()
+	{
+	if (!Protocol()->DstCache() || !iFlow.FlowContext())
+		{
+		return;
+		}
+
+	const TInetAddr& dstaddr = iFlow.FlowContext()->RemoteAddr();
+	if (dstaddr.IsUnspecified())
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] StoreDestinationCache() : No destination address"), (TInt)this));
+		return;
+		}
+
+	TInt err = KErrNone;
+	MDestinationCache *dcache = NULL;
+	TRAP(err, dcache = IMPORT_API_L(Protocol()->Interfacer(), MDestinationCache));
+	if (err != KErrNone || dcache == NULL)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] StoreDestinationCache() : DstCache not available"), (TInt)this));
+		return;
+		}
+
+	TCacheInfo cinfo;
+	cinfo.ClearAll();
+	cinfo.iMetrics[TCacheInfo::ESsThresh] = iSsthresh;
+	cinfo.iMetrics[TCacheInfo::ESRtt] = iSRTT;
+	cinfo.iMetrics[TCacheInfo::ERto] = iRTO;
+
+	TRAP(err, dcache->StoreL(dstaddr, cinfo));
+	if (err != KErrNone)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] StoreDestinationCache() : DstCache store failed"), (TInt)this));
+		return;
+		}
+	}
+
+
+void CProviderTCP6::Start()
+	{
+	LOG(Log::Printf(_L("Start\ttcp SAP[%u] enter"), (TInt)this));
+	CProviderInet6Transport::Start();
+	iFlags.iStarted = ETrue;
+
+	//
+	// Detach from parent socket
+	//
+	if (iParent)
+		{
+		iSockFlags.iNotify = ETrue;
+		iParent->DetachChild(this);
+
+		//
+		// Tell ESock which network interface the child socket is using.
+		//
+		const MInterface *iface = Protocol()->Interfacer()->Interface(iFlow.FlowContext()->Interface());
+		if (iface != NULL)
+			{
+			TPckgBuf<TSoIfConnectionInfo> netinfo;
+			netinfo().iIAPId = iface->Scope(EScopeType_IAP);
+			netinfo().iNetworkId = iface->Scope(EScopeType_NET);
+			LOG(Log::Printf(_L("\ttcp SAP[%u] Bearer IAP=%d, NID=%d"), (TInt)this, netinfo().iIAPId, netinfo().iNetworkId));
+			iSocket->Bearer(netinfo);
+			}
+		//
+		// Report error if TCP has been disconnected already
+		//
+		if (InState(ETcpClosed))
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] %s, Error %d"), (TInt)this, TcpState(), KErrDisconnected));
+			Error(KErrDisconnected);
+			}
+		}
+
+	//
+	// If an error has occurred, we must deliver it to the
+	// socket server now that the socket notifier has been
+	// initialised.
+	//
+	if (iLastError.iStatus != KErrNone && iErrorMask != 0)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Delayed error %d, mask %b delivered."),
+			(TInt)this, iLastError.iStatus, iErrorMask));
+		iSocket->Error(iLastError.iStatus, iErrorMask);
+		}
+
+	// Wake up the receiver. We might have something to do.
+	iRecvQ.Wake();
+	LOG(Log::Printf(_L("Start\ttcp SAP[%u] exit"), (TInt)this));
+	}
+
+
+//
+// Stop all transmission (except for iSendQ, which will be allowed to drain)
+//
+void CProviderTCP6::Stop()
+	{
+	if (iTransmitter)
+		CancelTransmit();
+	if (iRetransTimer)
+		CancelRetransmit();
+	if (iDelayAckTimer)
+		CancelDelayACK();
+	if (iLingerTimer)
+		iLingerTimer->Cancel();
+	}
+
+
+//
+// Empty all queues
+//
+void CProviderTCP6::FreeQueues()
+	{
+	iSockInQ.Free();
+	iSockInQLen = 0;
+	iSockOutQ.Free();
+	iSockOutQLen = 0;
+	iFragQ.Free();
+	iRecvQ.Free();
+	iSendQ.Cancel();
+	iSendQ.Free();
+	}
+
+
+//
+// Close the socket down
+//
+void CProviderTCP6::Close()
+	{
+	StoreDestinationCache();
+
+	iSockFlags.iSendClose = ETrue;
+	iSockFlags.iRecvClose = ETrue;
+	Protocol()->UnbindProvider(this);
+	Stop();
+	FreeQueues();
+	EnterState(ETcpClosed);
+	}
+
+
+void CProviderTCP6::Ioctl(TUint aLevel, TUint aName, TDes8* aOption)
+	{
+	LOG(Log::Printf(_L("Ioctl\ttcp SAP[%u] %x, %x"), (TInt)this, aLevel, aName));
+	if (aLevel == KSolInetTcp && aName == KIoctlTcpNotifyDataSent)
+		{
+		iFlags.iDataSentIoctl = ETrue;
+		CompleteIoctl(KErrNone);
+		}
+	else
+		CProviderInet6Transport::Ioctl(aLevel, aName, aOption);
+	}
+
+
+void CProviderTCP6::CancelIoctl(TUint aLevel, TUint aName)
+	{
+	LOG(Log::Printf(_L("CancelIoctl\ttcp SAP[%u] %x, %x"), (TInt)this, aLevel, aName));
+	if (aLevel == KSolInetTcp && aName == KIoctlTcpNotifyDataSent)
+		CompleteIoctl(KErrCancel);
+	else
+		CProviderInet6Transport::CancelIoctl(aLevel, aName);
+	}
+
+
+TInt CProviderTCP6::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+	{
+	TInt ret = KErrNotSupported;
+
+	if (aLevel == KSolInetTcp)
+		{
+		TInt intValue;
+		ret = GetOptionInt(aOption, intValue);
+
+#ifdef _LOG
+		if (ret == KErrNone)
+			Log::Printf(_L("SetOpt\ttcp SAP[%u] KSolInetTcp, %d, %d"), (TInt)this, aName, intValue);
+		else
+			Log::Printf(_L("SetOpt\ttcp SAP[%u] KSolInetTcp, %d"), (TInt)this, aName);
+#endif
+
+		switch (aName)
+			{
+		case KSoTcpAsync2MslWait:
+			// Not implemented. Not very useful.
+			ret = KErrNotSupported;
+			break;
+
+		case KSoTcpKeepAlive:
+			if (ret == KErrNone)
+				{
+				if (!intValue)
+					{
+					iFlags.iHaveKeepAlive = FALSE;
+					iFlags.iHaveTriggeredKeepAlive = FALSE;
+					}
+				if (intValue & 1)
+					iFlags.iHaveKeepAlive = TRUE;
+				if (intValue & 2)
+					iFlags.iHaveTriggeredKeepAlive = TRUE;
+				}
+			break;
+
+		case KSoTcpMaxSegSize:
+			if (ret == KErrNone)
+				{
+				if (!InState(ETcpInitial))
+					ret = KErrLocked;
+				else if (intValue < STATIC_CAST(TInt, KTcpMinimumMSS))
+					ret = KErrArgument;
+				else
+					{
+					iMSS = intValue;
+					iSMSS = iMSS;
+					iRMSS = iMSS;
+					}
+				}
+			break;
+
+		case KSoTcpNextSendUrgentData:
+			if (ret == KErrNone)
+				iFlags.iNextIsUrgent = intValue ? TRUE : FALSE;
+			break;
+
+		case KSoTcpNoDelay:
+			if (ret == KErrNone)
+				iFlags.iNoDelay = intValue ? TRUE : FALSE;
+			break;
+
+		case KSoTcpCork:
+		case KSoTcpNoPush:
+			if (ret == KErrNone)
+				{
+				iFlags.iCork = intValue ? TRUE : FALSE;
+				if (iFlags.iCork == EFalse && aName == KSoTcpCork)
+					{
+					// When turning Cork off, send pending data from output queue immediately.
+					// This is the only difference between Cork and NoPush
+					SchedTransmit();
+					}
+				}
+			break;
+
+		case KSoTcpOobInline:
+			if (ret == KErrNone)
+				iFlags.iOobInline = intValue ? TRUE : FALSE;
+			break;
+		
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW	
+		case KSoTcpMaxRecvWin:
+			//Case to set the Max window size from shim.
+			if(ret == KErrNone)
+				{
+				if (InState(ETcpClosed|ETcpFinWait2|ETcpCloseWait|ETcpLastAck))
+					ret = KErrLocked;
+				else	
+				    if(intValue > iTcpMaxRecvWin)
+				    {
+				    iTcpMaxRecvWin = intValue;
+				    }
+				
+				}
+			break;
+		case KSoTcpRecvWinAuto:
+		    {
+		    // If user sets the window size, we have to give the preference for user setting. Automatic
+		    // window setting will be then disabled by default, once user calls the SetOption to set the 
+		    // receive window, this feature will be diabled by default till the socket is opened.
+		    if(iWindowSetByUser)
+		        {
+		        return KErrNone;
+		        }
+		    else if (InState(ETcpClosed|ETcpFinWait2|ETcpCloseWait|ETcpLastAck))
+		        ret = KErrLocked;
+            else if (intValue < STATIC_CAST(TInt, KTcpMinimumWindow))
+                iSockInBufSize = KTcpMinimumWindow;
+            else
+                {
+
+		        //If new TCP window is larger then the previous window, increase the 
+		        //iSockInBufSize right now. TCP recv function takes  care of
+		        //advertising a new effective TCP window. 
+		            if (intValue >= iSockInBufSize)
+		                {
+		                //Make it Zero so TCP could avoid the
+		                //TCP window shrinking processing in Recv.  
+		                iNewTcpWindow = 0;
+		                //FreeWindow has to be increased at the same time.
+		                iFreeWindow += intValue - iSockInBufSize;
+		                // Make the new TCP receive buffer change effective now.
+		                iSockInBufSize = intValue;                                                          
+		                }
+		            else
+		                {
+		                //This sets iNewTcpWindow to a non-zero value, which indicates 
+		                //to the TCP that window is shrunk and process TCP segments
+		                //which are in air before setting a new TCP receive buffer.         
+		                //TCP Receive window starts moving only when TCP hidden window
+		                //size exceeds the size of the shrunk window.
+		                   
+		                iNewTcpWindow = intValue;
+		                //Even in case of window shrink we can set the receive buffer size
+		                //immediately. This will be helpful, for processing SYN-ACK and other
+		                //receiver side processing.
+		                //For already connected sockets iNewTcpWindow will be taking care
+		                //of shrinking the window size for that TCP session.
+		                iSockInBufSize = iNewTcpWindow;
+		                if( iAdvertisedWindow > iNewTcpWindow )
+		                    {
+		                    iShrinkedWindowSize = iAdvertisedWindow - iNewTcpWindow;
+		                    }
+		                else
+		                    {
+		                    // No Need to process TCP receive window processing.
+		                    iNewTcpWindow = 0;
+		                    }
+		                }
+					
+                } 
+
+		    }
+		    break;
+#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+			case KSoTcpRecvWinSize:
+			    {
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW  
+                iWindowSetByUser = ETrue;
+#endif // SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW                
+                if (ret == KErrNone)
+                    {
+                    if (!InState(ETcpInitial))
+                        ret = KErrLocked;
+                    else if (intValue < STATIC_CAST(TInt, KTcpMinimumWindow))
+                        iSockInBufSize = KTcpMinimumWindow;
+                    else
+                        {
+                        iSockInBufSize = intValue;
+                        }
+                    }
+			    }
+			break;
+
+		case KSoTcpSendWinSize:
+			if (ret == KErrNone)
+				{
+				if (!InState(ETcpInitial))
+					ret = KErrLocked;
+				else if (intValue < STATIC_CAST(TInt, KTcpMinimumWindow))
+					iSockOutBufSize = KTcpMinimumWindow;
+				else
+					iSockOutBufSize = intValue;
+				}
+			break;
+
+		case KSoTcpLinger:
+			if (aOption.Length() < (TInt)sizeof(TSoTcpLingerOpt))
+				{
+				return KErrArgument;
+				}
+			if (iSockFlags.iSendClose)
+				{
+				return KErrInUse;
+				}
+
+				{
+				TSoTcpLingerOpt *opt = (TSoTcpLingerOpt *)aOption.Ptr();
+				if (opt->iOnOff != 0 && opt->iLinger >= 0)
+					{
+					if (opt->iLinger > KTcpMaxLingerTime)
+						{
+						return KErrArgument;
+						}
+					iLinger = opt->iLinger;  // linger enabled.
+					}
+				else
+					{
+					iLinger = -1;  // linger disabled.
+					}
+				}
+			break;
+
+		default:
+			ret = KErrNotSupported;
+			break;
+			}
+		}
+
+	if (aLevel == KSolInetIp)
+		{
+		TInt intValue;
+		ret = GetOptionInt(aOption, intValue);
+
+		switch(aName)
+        	{
+		case KSoIpTOS:
+			// Silently filter out the ECN bits from TOS setting.
+            // We're assuming that Protocol->Ecn() (based on ini params) holds the correct
+            // values for ECN bits.
+            	{
+	            intValue = (intValue & 0xfc) | Protocol()->Ecn();
+            	TPckgBuf<TInt> tosopt(intValue);
+
+	            // Rest of the option processing is done at the lower levels.
+    	        return CProviderInet6Transport::SetOption(aLevel, aName, tosopt);
+    	        }
+
+		case KSoHeaderIncluded:
+		case KSoRawMode:
+			// The base class implements HeaderIncluded and RawMode by default.
+			// Force "Not Supported" for TCP here!
+			return KErrNotSupported;
+		default:
+        	ret = KErrNotSupported;
+            }
+		}
+
+
+	if (ret == KErrNotSupported)
+		ret = CProviderInet6Transport::SetOption(aLevel, aName, aOption);
+	
+	return ret;
+	}
+
+
+TInt CProviderTCP6::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
+	{
+	LOG(Log::Printf(_L("GetOpt\ttcp SAP[%u] %d, %d"), (TInt)this, aLevel, aName));
+	CProviderTCP6 *This = CONST_CAST(CProviderTCP6*, this);
+	TInt ret = KErrNotSupported, urgentChar;
+
+	switch (aLevel)
+		{
+	case KSOLSocket:
+		switch (aName)
+			{
+		case KSOReadBytesPending:
+			ret = SetOptionInt(aOption, iPending);
+			break;
+
+		case KSOUrgentDataOffset:
+			ret = SetOptionInt(aOption, iUpCount ? UrgentOffset() - iUpCount + 1 : 0);
+			LOG(Log::Printf(_L("\ttcp SAP[%u] Urgent data offset = %d"),
+				(TInt)this, iUpCount ? UrgentOffset() : 0));
+			break;
+
+		default:
+			break;
+			}
+		break;
+
+	case KSolInetTcp:
+		switch (aName)
+			{
+		case KSoTcpAsync2MslWait:
+			// XXX - Not implemented. Not very useful.
+			break;
+
+		case KSoTcpKeepAlive:
+			ret = SetOptionInt(aOption, iFlags.iHaveKeepAlive);
+			break;
+
+		case KSoTcpMaxSegSize:
+			ret = SetOptionInt(aOption, iMSS);
+			break;
+
+		case KSoTcpNextSendUrgentData:
+			ret = SetOptionInt(aOption, iFlags.iNextIsUrgent);
+			break;
+
+		case KSoTcpNoDelay:
+			ret = SetOptionInt(aOption, iFlags.iNoDelay);
+			break;
+
+		case KSoTcpCork:
+		case KSoTcpNoPush:
+			ret = SetOptionInt(aOption, iFlags.iCork);
+			break;
+
+		case KSoTcpOobInline:
+			ret = SetOptionInt(aOption, iFlags.iOobInline);
+			break;
+
+		case KSoTcpRecvWinSize:
+			ret = SetOptionInt(aOption, iSockInBufSize);
+			break;
+
+		case KSoTcpSendWinSize:
+			ret = SetOptionInt(aOption, iSockOutBufSize);
+			break;
+
+		case KSoTcpListening:
+			ret = SetOptionInt(aOption, InState(ETcpListen) ? 1 : 0);
+			break;
+
+		case KSoTcpNumSockets:
+			ret = SetOptionInt(aOption, Protocol()->SapCount());
+			break;
+
+		case KSoTcpLinger:
+			if (aOption.MaxLength() < (TInt)sizeof(TSoTcpLingerOpt))
+				{
+				return KErrTooBig;
+				}
+
+			TSoTcpLingerOpt opt;
+			if (iLinger == -1)
+				{
+				opt.iOnOff = 0;
+				opt.iLinger = 0;
+				}
+			else
+				{
+				opt.iOnOff = 1;
+				opt.iLinger = iLinger;
+				}
+			aOption.SetLength(sizeof(opt));
+			aOption.Copy((TUint8*)&opt, sizeof(opt));
+			ret = KErrNone;
+			break;
+
+		case KSoTcpPeekUrgentData:
+			if (ret = This->GetUrgent(urgentChar, KSockReadPeek), ret == KErrNone)
+				ret = SetOptionInt(aOption, urgentChar);
+			break;
+
+		case KSoTcpRcvAtMark:
+			ret = SetOptionInt(aOption, (UrgentOffset() == 0) ? 1 : 0);
+			break;
+
+		case KSoTcpReadBytesPending:
+			ret = SetOptionInt(aOption, iPending);
+			break;
+
+		case KSoTcpReadUrgentData:
+			if (ret = This->GetUrgent(urgentChar, 0), ret == KErrNone)
+				ret = SetOptionInt(aOption, urgentChar);
+			break;
+
+		case KSoTcpSendBytesPending:
+			ret = SetOptionInt(aOption, iSockOutQLen);
+			break;
+
+		default:
+			break;
+			}
+		break;
+
+	case KSolInetIp:
+		switch(aName)
+			{
+        case KSoIpTOS:
+			// Clear the ECN bits from the returned value before delivering it to socket.
+            ret = CProviderInet6Transport::GetOption(aLevel, aName, aOption);
+            if (ret != KErrNone)
+            	{
+                TInt intValue;
+                if (GetOptionInt(aOption, intValue) == KErrNone)
+                	ret = SetOptionInt(aOption, intValue & 0xfc);
+                }
+            break;
+		case KSoHeaderIncluded:
+		case KSoRawMode:
+			// The base class implements HeaderIncluded and RawMode by default.
+			// Force "Not Supported" for TCP here!
+			return KErrNotSupported;
+        default:
+        	break;
+			}
+		break;
+
+	default:
+		break;
+		}
+
+	if (ret == KErrNotSupported)
+		ret = CProviderInet6Transport::GetOption(aLevel, aName, aOption);
+
+	return ret;
+	}
+
+
+TInt CProviderTCP6::SetRemName(TSockAddr &aAddr)
+	{
+	TInt err;
+	TInetAddr addr = aAddr;
+
+	// Check port range
+	if (addr.Port() < 1 || addr.Port() > 65535)
+		return KErrGeneral;
+
+	// Check address
+	if (addr.IsUnspecified() || !addr.IsUnicast())
+		return KErrBadName;	
+
+	if (addr.Family() == KAfInet)
+		addr.ConvertToV4Mapped();
+
+	TInt family = addr.IsV4Mapped() ? KAfInet : KAfInet6;
+
+	if(iSockFamily == KAFUnspec)
+		iSockFamily = family;
+	else if (iSockFamily != family)
+		return KErrBadName;		
+		
+	//
+	// If we're reusing a local address we must check for an existing
+	// connection before we can accept the remote address.
+	//
+	if (iSockFlags.iReuse && iFlow.FlowContext()->LocalPort() != KInetPortNone)
+		{
+		if (Protocol()->LocateSap(EMatchConnection, KAFUnspec, iFlow.FlowContext()->LocalAddr(), addr))
+			return KErrInUse;
+		}
+
+	// Set remote address and port.. use original to get iAppFamily valid.
+	if (err = CProviderInet6Transport::SetRemName(aAddr), err != KErrNone)
+		return err;
+
+	return KErrNone;
+	}
+
+
+void CProviderTCP6::ActiveOpen()
+	{
+	LOG(Log::Printf(_L("ActiveOpen\ttcp SAP[%u]"), (TInt)this));
+	ASSERT(InState(ETcpInitial));
+
+	TInt status = iFlow.Connect();
+	if (status < 0)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Flow status = %d"), (TInt)this, status));
+		iSockFlags.iConnected = EFalse;
+		Error(status, MSocketNotify::EErrorConnect);
+		return;
+		}
+
+	// Store KeepInterfaceUp to be restored when entering Established state.
+	StoreKeepInterfaceUp();
+
+	EnterState(ETcpConnect);
+	if (status == EFlow_READY)
+		SendSYN();
+	}
+
+
+TInt CProviderTCP6::PassiveOpen(TUint aQueSize)
+	{
+	ASSERT(InState(ETcpInitial));
+	ASSERT(!iListenQueue);
+	LOG(Log::Printf(_L("PassiveOpen\ttcp SAP[%u] QueSize=%d"), (TInt)this, aQueSize));
+
+	//
+	// On EPOC you can only call listen once, so it is safe to do this here.
+	//
+	iListenQueue = new CProviderTCP6*[aQueSize];
+	if (!iListenQueue)
+		return KErrNoMemory;
+
+	iListenQueueSize = aQueSize;
+	for (TUint i=0; i < iListenQueueSize; i++)
+		iListenQueue[i] = 0;
+	EnterState(ETcpListen);
+	return KErrNone;
+	}
+
+//
+// This routine tries to detach the socket from the socket server.
+// Detach() is called from Shutdown(), when the user has called
+// Close() or Shutdown(ENormal).
+//
+// If linger timeout is active, Shutdown() will complete with
+// error KErrWouldBlock.
+//
+void CProviderTCP6::Detach()
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] Detach()"), (TInt)this));
+
+	StoreDestinationCache();
+
+	CompleteIoctl(KErrCancel);
+	if (iLinger > 0)
+		{
+		iLingerTimer->Cancel();
+		Error(KErrWouldBlock, MSocketNotify::EErrorClose);
+		}
+	iLinger = -1;
+	NoSecurityChecker();				// The checker will be unusable.
+	iSocket->CanClose(MSocketNotify::EDetach);
+	iSockFlags.iNotify = EFalse;            // No more upcalls
+	iSockFlags.iAttached = (iSocket != 0);  // Did we get detached?
+	LOG(if (iSockFlags.iAttached) Log::Printf(_L("\ttcp SAP[%u] DETACH FAILED!"), (TInt)this));
+	ASSERT(!iSockFlags.iAttached);
+	}
+
+void CProviderTCP6::Expire()
+	{
+	if (!iSockFlags.iAttached)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Expire(): SELF DESTRUCT!"), (TInt)this));
+		delete this;
+		return;
+		}
+	if (iSockFlags.iNotify)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Expire(): DISCONNECT!"), (TInt)this));
+		Close();
+		iSockFlags.iNotify = EFalse;
+		iSocket->Disconnect();
+		}
+	}
+
+void CProviderTCP6::Shutdown(TCloseType aOption)
+	{
+	LOG(Log::Printf(_L("Shutdown\ttcp SAP[%u] TCloseType=%d"), (TInt)this, aOption));
+
+	switch(aOption)
+		{
+	case ENormal:
+		if (InState(ETcpListen|ETcpInitial|ETcpConnect|ETcpSynSent|ETcpClosed))
+			{
+			// Just do a brutal shutdown in these states.
+			Close();
+			break;
+			}
+		
+		// No need to negotiate parameters as we only want to notify other end point.
+		if (InState(ETcpSynReceived))
+			ClearSYNSettings();
+
+		// Send RST if receive queue is not empty.
+		if (iLinger == 0 || SockInQLen() || !iFragQ.IsEmpty())
+			{
+			SendReset(iSND.NXT);
+			if (FatalState())
+				break;
+
+			// If linger is enabled and timer==0, close the socket immediately
+			if (iLinger == 0)
+				{
+				Close();
+				break;
+				}
+			}
+
+		iSockFlags.iSendClose = ETrue;
+		iSockFlags.iRecvClose = ETrue;
+		iSockInQ.Free();
+		iSockInQLen = 0;
+		iFragQ.Free();
+		iNewData = 0;
+
+		if (iLinger == -1 || iSockOutQLen == 0)
+			{
+			Detach();
+			}
+		else
+			{
+			//
+			// Start linger timer. RSocket::Close() returns when timer
+			// expires or when all data has been succesfully transmitted.
+			//
+			iLingerTimer->Start(iLinger * KOneSecondUs);
+			}
+		SchedTransmit();
+
+		break;
+
+	case EStopInput:
+	case EStopOutput:
+		if (InState(ETcpListen|ETcpInitial))
+			{
+			Error(KErrNotSupported, MSocketNotify::EErrorClose);
+			return;
+			}
+
+		if (aOption == EStopInput)
+			{
+			iSockFlags.iRecvClose = ETrue;
+
+			// Send RST if receive queue is not empty.
+			if (SockInQLen() || !iFragQ.IsEmpty())
+				{
+				SendReset(iSND.NXT);
+				if (FatalState())
+					break;
+				}
+			iSockInQ.Free();
+			iFragQ.Free();
+			iNewData = 0;
+			}
+		else
+			{
+			// HalfDuplex Close and simultaneous SYN_RCVD -> FIN_WAIT_1 is not supported.
+			iSockFlags.iSendClose = ETrue;
+			SchedTransmit();
+			}
+
+		Error(KErrNone, MSocketNotify::EErrorClose);
+		Nif::SetSocketState(ENifSocketConnected, this);
+		break;
+
+	case EImmediate:
+		if (InState(ETcpSynReceived|ETcpEstablished|ETcpFinWait1|ETcpFinWait2|ETcpCloseWait))
+			SendReset(iSND.NXT);
+		CompleteIoctl(KErrCancel);
+		Close();
+		return;
+
+	default:
+		Panic(EInet6Panic_NotSupported);
+		break;
+		}
+
+	//
+	// If we encountered a fatal error during the above processing,
+	// stop everything and enter CLOSED state.
+	//
+	if (FatalState())
+		Close();
+
+	if (InState(ETcpFinWait2) && iSockFlags.iRecvClose)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Setting FIN-WAIT-2 timeout"), (TInt)this));
+		SchedMsl2Wait();
+		}
+
+	//
+	// If we're in CLOSED state and we're not detached,
+	// tell the socket server that we're ready to die.
+	//
+	if (iSocket && InState(ETcpClosed))
+		iSocket->CanClose();
+
+	//
+	// Note!  The socket server can immediately delete the socket
+	//        within CanClose(). Don't add anything here!
+	//
+	}
+
+
+//
+// PRTv1.0 API
+//
+TUint CProviderTCP6::Write(const TDesC8 & aDesc, TUint aOptions, TSockAddr* /*aAddr*/ /*=NULL*/)
+	{
+	TDualBufPtr buf(aDesc);
+	return Send(buf, aDesc.Length(), aOptions);
+	}
+
+void CProviderTCP6::GetData(TDes8 & aDesc, TUint aOptions, TSockAddr* /*aAddr*/)
+	{
+	TDualBufPtr aBuf(aDesc);
+	Recv(aBuf, aDesc.Length(), aOptions);
+	}
+
+//
+// PRTv1.5 API
+//
+TInt CProviderTCP6::Write(RMBufChain& aData, TUint aOptions, TSockAddr* /* anAddr*/)
+	{
+	TDualBufPtr buf(aData);
+	return Send(buf, aData.Length(), aOptions);
+	}
+
+TInt CProviderTCP6::GetData(RMBufChain& aData, TUint aLength, TUint aOptions, TSockAddr* /*aAddr*/)
+	{
+	TDualBufPtr aBuf(aData);
+	return Recv(aBuf, aLength, aOptions);
+	}
+
+TInt CProviderTCP6::Send(TDualBufPtr& aBuf, TInt aLength, TUint aOptions)
+	{
+	LOG(Log::Printf(_L("Write\ttcp SAP[%u] len=%d, options=%d"), (TInt)this, aLength, aOptions));
+	ASSERT(aLength > 0);
+
+	//
+	// Limit queue size to maximum socket buffer size rounded down to nearest multiple segment.
+	// Note: during fast recover we allow the socket buffer to expand in order to have enough
+	// data to keep the transmission going.
+	//
+	TInt effMSS = EffectiveMSS();
+	TInt bufSize = iSockOutBufSize +
+		(iSacked.Count() ? (iSacked.Last()->iRight - iSND.UNA) : (iDupAcks * iSMSS));
+	TInt reserve = (bufSize - (iSND.NXT - iSND.UNA)) % effMSS;
+	TInt space = bufSize - iSockOutQLen - reserve;
+
+	LOG(Log::Printf(_L("\ttcp SAP[%u] len=%d space=%d"), (TInt)this, aLength, space));
+
+	if ((aOptions & KSockWriteUrgent) || iFlags.iNextIsUrgent)
+		{
+		//
+		// According to RFC1122 the urgent pointer should point to the
+		// last byte of urgent data. However, practically all TCP stacks
+		// today choose to conform to BSD unix and follow the original
+		// behaviour stated in RFC793. Therefore, we set the urgent pointer
+		// to point to the first non-urgent byte following the urgent data.
+		//
+		iFlags.iNextIsUrgent = EFalse;
+		iSND.UP = iSND.UNA + iSockOutQLen + aLength;
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Urgent pointer set, seq = %u"), (TInt)this, iSND.UP.Uint32()));
+		}
+
+	if (space <= 0)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] FLOW STOPPED"), (TInt)this));
+		iSockFlags.iFlowStopped = ETrue;
+		return 0;
+		}
+
+	// Append data to socket queue. 
+	aLength = aBuf.Append(iSockOutQ, (aLength < space) ? aLength : space);
+	iSockOutQLen += aLength;
+
+	// We make a simple Nagle check here to avoid starting the transmitter unnecessarily.
+	// To spell it out:
+	// - If Cork option is set, always wait for full-sized segment before sending.
+	// - Without Cork option one under-sized segment is allowed to be outstanding.
+	// - TODO: With NoPush option segments are only sent when send buffer is full or
+	//   when the connection is closed.
+	if (!iSND.WND)
+		{
+		// Start probing
+		SchedRetransmit();
+		}
+	else if ((iPartialSeq <= iSND.UNA && !iFlags.iCork) ||
+		iSND.UNA + iSockOutQLen + aLength >= iSND.NXT + effMSS ||
+		iFlags.iNoDelay)
+		{
+		// Start transmitter
+		SchedTransmit();
+		}
+
+	return aLength;
+	}
+
+
+TInt CProviderTCP6::Recv(TDualBufPtr& aBuf, TInt aLength, TUint aOptions)
+	{
+	LOG(Log::Printf(_L("GetData\ttcp SAP[%u] len=%d, options=%d"), (TInt)this, aLength, aOptions));
+
+	// Update iPending
+	if (!(aOptions & KSocketInternalReadBit))
+		iPending -= aLength;
+
+	// This might happen when aborting a connection.
+	if (iSockInQ.IsEmpty())
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] No data available!"), (TInt)this));
+		return 0;
+		}
+
+	//
+	// Discard in-band urgent data junk
+	//
+	while (iUpCount && UrgentOffset(0) == 0 && (!iFlags.iUrgentMode || UrgentOffset() > 0))
+		{
+		iSockInQ.TrimStart(1);
+		--iSockInQLen;
+		++iFreeWindow;
+		ForgetUrgentPointer();
+		}
+
+	ASSERT((aOptions & KSockReadPeek) || !iCopyOutOffset);
+	ASSERT(iCopyOutOffset + aLength <= (TInt)iSockInQLen);
+
+	//
+	// Peek. Copy from queue, fix counters and force receiver restart.
+	//
+	if (aOptions & KSockReadPeek)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Peek"), (TInt)this));
+
+		TInt err = aBuf.CopyIn(iSockInQ, iCopyOutOffset, aLength);
+		if (err != KErrNone)
+			return KErrNoMBufs;
+
+		if (!(aOptions & KSocketInternalReadBit))
+			{
+			iFlags.iCompleteRecv = ETrue;
+			iNewData += aLength;
+			iCopyOutOffset += aLength;
+			iRecvQ.Wake();
+			}
+
+		return aLength;
+		}
+
+	//
+	// Normal read. Consume data from the queue.
+	//
+	// Note: aBuf.Consume() may return less than we asked
+	// if it is unable to allocate an MBuf for splitting the
+	// inbound queue. This can only happen with PRTv1.5.
+	// If we get nothing at all we return KErrNoMBufs here.
+	//
+	aLength = aBuf.Consume(iSockInQ, aLength, iBufAllocator);
+	if (aLength == 0)
+		return KErrNoMBufs;
+
+	//
+	// If we are now reading the actual urgent data, force the
+	// application level read operation to complete and exit
+	// urgent mode.
+	//
+	if (iFlags.iUrgentMode && UrgentOffset() == 0)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Reading urgent data. Clear urgent mode"), (TInt)this));
+		iFlags.iUrgentMode = EFalse;
+		iFlags.iCompleteRecv = !(aOptions & KSocketInternalReadBit);
+		ForgetUrgentPointer();
+		}
+		
+	iSockInQLen -= aLength;
+	
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+	//Application is reading the data from the receive Q buffer
+	//We need to make sure, that we will show that window is not shrinked
+	//to the sender, so tha right edge of the sender window remains constant.
+	//if This is true, then it is a case of TCP window shrink and we need 
+	//to handle it.
+	if ( iNewTcpWindow )
+		{
+	   	//Log  this message for information, that Window is shrinked
+	   	LOG(Log::Printf(_L("\ttcp SAP[%u] TCP window shrinking mode on"), (TInt)this));
+	   
+	   	//Increase the hidden free TCP receive window.
+	   	iHiddenFreeWindow += aLength;
+	   
+	   	if (iHiddenFreeWindow >= iShrinkedWindowSize)
+			{
+			//Disable window shrink processing, so that TCP could switch
+			//to the normal processing.    
+			iSockInBufSize = iNewTcpWindow;
+			
+			//Add the usable window to the free window.
+			iFreeWindow += iHiddenFreeWindow - iShrinkedWindowSize;
+			
+			//There are chances that TCP receive window might further shrink.
+			iHiddenFreeWindow = 0;
+			
+			//TCP Receive window shrink phase is over.
+			iNewTcpWindow = 0;
+			
+			//Log  this message for information, that Window is shrinked
+			LOG(Log::Printf(_L("\ttcp SAP[%u] TCP window shrinking mode off"), (TInt)this));
+			}
+		}
+	else
+#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+	
+		{
+		iFreeWindow += aLength;	
+		}
+
+	//
+	// If we have read everything upto the urgent data,
+	// force the application level read to complete.
+	// If the urgent data has not yet been read, signal
+	// the application again (read completion will clear
+	// the signal).
+	//
+	if (UrgentOffset() == 0)
+		{
+		iFlags.iCompleteRecv = ETrue;
+		if (iFlags.iUrgentMode)
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] Now at urgent data"), (TInt)this));
+			iFlags.iNotifyUrgent = ETrue;
+			}
+		}
+
+	//
+	// Increase receive window in multiples of RMSS
+	//
+	// The following assumes that the path MTU is symmetric and that
+	// the peer uses the same options as we do. We could use some
+	// kind of peer effective MSS detection heuristic in order to
+	// make this smarter.
+	//
+	TUint effMSS = EffectiveMSS();
+	if (iFreeWindow >= effMSS)
+		{
+		//
+		// Round down to nearest multiple of RMSS. However, be careful not to
+		// shrink a previously advertised window.
+		//
+		iFreeWindow += iRCV.WND;
+		iRCV.WND = Max(iRCV.WND, effMSS * (iFreeWindow / effMSS));
+		iFreeWindow -= iRCV.WND;                   // Leave reminder for later.
+
+		//
+		// If our last advertised window is so small that the sender might
+		// block before we would normally send our next ack, we will send
+		// a window update. This will prevent the sender from unnecessarily
+		// going into probe mode. We will only do this if we can advertise
+		// at least half a window.
+		//
+		if (InState(ETcpEstablished|ETcpFinWait1|ETcpFinWait2))
+			{
+			if ((TInt)iAdvertisedWindow <= iSockInBufSize/4 && iRCV.WND >= iSockInBufSize/2)
+				{
+				LOG(Log::Printf(_L("\ttcp SAP[%u] Sending window update %d -> %d"), (TInt)this, iAdvertisedWindow, iRCV.WND));
+				SendSegment(KTcpCtlACK);
+				}
+			}
+		}
+
+	//
+	// If we have data that has not yet been announced to the socket server,
+	// make sure we get around to it.
+	//
+	if (iNewData)
+		iRecvQ.Wake();
+
+	LOG(Log::Printf(_L("GetData\ttcp SAP[%u] returns %d bytes, %d bytes pending"), (TInt)this, aLength, iPending));
+	return aLength;
+	}
+
+
+//
+// Process ICMP Error. Treat it as a soft error unless we're trying to connect.
+//
+void CProviderTCP6::IcmpError(TInt aError, TUint aOperationMask, TInt aType, TInt aCode,
+	const TInetAddr& aSrcAddr, const TInetAddr& aDstAddr, const TInetAddr& aErrAddr)
+	{
+	CProviderInet6Transport::IcmpError(aError,InState(ETcpSynSent|ETcpSynReceived) ?
+		MSocketNotify::EErrorAllOperations : aOperationMask,
+		aType, aCode, aSrcAddr, aDstAddr, aErrAddr);
+	}
+
+//
+// This routine maintains a sorted rotating array of detected urgent pointers.
+// The lowest detected UP is always stored in iUpArray[iUpIndex] and the highest
+// one is stored in iUpArray[(iUpIndex + iUpCount - 1) % KTcpUpMax].
+// If the array overflows, the lowest UP is always discarded.
+//
+void CProviderTCP6::RememberUrgentPointer(TTcpSeqNum aUp)
+	{
+	// Insert new UP into the sorted list.
+	TInt i;
+	TTcpSeqNum up;
+	
+	for (i = iUpIndex + iUpCount; i > iUpIndex; --i)
+		{
+		up = iUpArray[(i - 1) % KTcpUpMax];
+		if (aUp > up)
+			break;
+		if (aUp == up)
+			return;
+		}
+
+	if (i == iUpIndex && iUpCount == KTcpUpMax)
+		return;
+	TUint index;
+	while (i < iUpIndex + iUpCount)
+		{
+		index = i % KTcpUpMax;
+		up = iUpArray[index];
+		iUpArray[index] = aUp;
+		aUp = up;
+		++i;
+		}
+
+	iUpArray[i % KTcpUpMax] = aUp;
+	if (iUpCount < KTcpUpMax)
+	    {
+	    ++iUpCount;
+	    }
+	else
+	    {
+	    iUpIndex += 1;
+	    if(iUpIndex >= KTcpUpMax)
+	        iUpIndex -= KTcpUpMax;
+	    }
+
+	iFlags.iUrgentMode = ETrue;
+	iFlags.iNotifyUrgent = !iFlags.iOobInline;
+	}
+
+void CProviderTCP6::ForgetUrgentPointer()
+	{
+	if (iUpCount)
+		{
+		if (++iUpIndex >= KTcpUpMax)
+			iUpIndex = 0;
+		--iUpCount;
+		}
+	}
+
+
+TInt CProviderTCP6::GetUrgent(TInt& aUrgentChar, TUint aOptions)
+	{
+	TTcpSeqNum up;
+	RMBuf *m;
+	TBool found = EFalse;
+	TInt urgentOffset = UrgentOffset();
+
+	if (!iFlags.iUrgentMode || iFlags.iOobInline)
+		return KErrNotFound;
+
+	up = UrgentHigh();
+	if (up <= iRCV.NXT)
+		{
+		// It's in the socket queue.
+		if (urgentOffset == 0 && !(aOptions & KSockReadPeek))
+			{
+			//
+			// It's first in queue. Fetch it with Recv(), so that receive
+			// window is correctly updated.
+			//
+			TBuf8<1> buf(1);
+			TDualBufPtr p(buf);
+			iNewData--;
+			Recv(p, 1, aOptions | KSocketInternalReadBit);
+			aUrgentChar = buf[0];
+			LOG(Log::Printf(_L("\ttcp SAP[%u] GetUrgent(): Urgent char first in socket queue = %02x"), (TInt)this, aUrgentChar));
+			found = ETrue;
+			}
+		else
+			{
+			for (m = iSockInQ.First(); m != NULL; m = m->Next())
+				{
+				if (urgentOffset < m->Length())
+					{
+					aUrgentChar = *(m->Ptr() + urgentOffset);
+					LOG(Log::Printf(_L("\ttcp SAP[%u] GetUrgent(): Urgent char in socket queue = %02x"), (TInt)this, aUrgentChar));
+					found = ETrue;
+					break;
+					}
+				urgentOffset -= m->Length();
+				}
+			}
+		}
+	else if (up <= iSND.WL1 + iRMSS)
+		{
+		// Maybe it's in the fragment queue.
+		TMBufPktQIter iter(iFragQ);
+		for (iter.SetToFirst(); iter.More(); iter++)
+			{
+			RMBufTcpFrag& frag = (RMBufTcpFrag&)iter.Current();
+			TTcpSeqNum seq = frag.Offset();
+			TUint32 len = frag.FragmentLength();
+
+			if (up <= seq)
+				break;
+
+			if (up <= seq + len)
+				{
+				TTcpPacket seg(frag);
+				RMBuf *m, *prev;
+				TInt off, len;
+				frag.Goto(seg.iHdr->HeaderLength() + (up - seq) - 1,
+					m, off, len, prev);
+				aUrgentChar = *(m->Ptr() + off);
+				LOG(Log::Printf(_L("\ttcp SAP[%u] GetUrgent(): Urgent char in fragment queue = %02x"), (TInt)this, aUrgentChar));
+				found = ETrue;
+				break;
+				}
+			}
+		}
+
+	if (!found)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] GetUrgent(): Would block"), (TInt)this));
+		return KErrWouldBlock;
+		}
+
+	if (!(aOptions & KSockReadPeek))
+		{
+		iFlags.iUrgentMode = EFalse;
+
+		// Clear urgent mode exception flag
+		if (iSockFlags.iNotify)
+			iSocket->Error(KErrNone, 0);
+		}
+
+	return KErrNone;
+	}
+
+
+void CProviderTCP6::Process(RMBufChain& aPacket, CProtocolBase* /*aSourceProtocol*/)
+	{
+	RMBufRecvInfo *const info = RMBufRecvPacket::PeekInfoInChain(aPacket);
+#ifdef SYMBIAN_NETWORKING_UPS
+	if (info == NULL || (!HasNetworkServices() && (ConnectionInfoSet() == EFalse) && (info->iFlags & KIpLoopbackPacket) == 0))
+#else
+	if (info == NULL || (!HasNetworkServices() && (info->iFlags & KIpLoopbackPacket) == 0))
+#endif
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Not allowed to receive external packets"), (TInt)this));
+		aPacket.Free();
+		return;
+		}
+	iRecvQ.Append(aPacket);
+	iRecvQ.Wake();
+	}
+
+
+void CProviderTCP6::ErrorExpire(TInt aError)
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] ErrorExpire(): Closing SAP on fatal error: %d"), (TInt)this, aError));
+	if (!iSockFlags.iAttached)
+		{
+		Expire();
+		return;
+		}
+	Close();
+	CProviderInet6Transport::Error(aError);
+	}
+
+void CProviderTCP6::CanSend()
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] CanSend()"), (TInt)this));
+
+	//
+	// If the flow has become unblocked, process all missed events
+	// and restart transmitter.
+	//
+	TInt flowStatus = iFlow.Status();
+	if (flowStatus < 0)
+		{
+		ErrorExpire(flowStatus);
+		return;
+		}
+
+	if (flowStatus == EFlow_READY)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] CanSend(): Flow UNBLOCKED"), (TInt)this));
+
+		if (InState(ETcpListen|ETcpConnect))
+			{
+			//
+			// Get the SAP started.
+			//
+			if (InState(ETcpConnect))
+				SendSYN();
+			else if (InState(ETcpListen) && !iRecvQ.IsEmpty())
+				iRecvQ.Wake();
+			}
+
+		if (iFlags.iRetransmitPending)
+			{
+			iFlags.iRetransmitPending = EFalse;
+			RetransmitSegments();
+			}
+
+		if (iFlags.iTransmitPending)
+			{
+			iFlags.iTransmitPending = EFalse;
+			SendSegments();
+			}
+
+		if (!iSendQ.IsEmpty())
+			iSendQ.Wake();
+			
+		if (CanTriggerKeepAlive())
+			{
+			// The heaviest time check only if we are otherwise allowed to send the keepalive.
+			TUint32 time_now = TimeStamp();
+			if (time_now - iLastTriggeredKeepAlive > KTcpKeepAliveTH * KOneSecondUs)
+				{
+				iLastTriggeredKeepAlive = time_now;
+				LOG(Log::Printf(_L("\ttcp SAP[%u] CanSend(): Sending a Keep-Alive probe"), (TInt)this));
+				SendSegment(KTcpCtlACK, iSND.UNA - 1, 0);
+				}
+			}
+		}
+	}
+
+
+//
+// This is the actual event driven asynchronous transmitter loop.
+//
+void CProviderTCP6::Transmit()
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] Transmit"), (TInt)this));
+
+	RMBufSendPacket packet;
+	TInt flowStatus;
+	LOG(TInt count = 0);
+
+	ASSERT(!iSendQ.IsEmpty());
+	RMBufSendInfo *info  = NULL;
+	// Transmit packets when flow is ready
+	while ((flowStatus = iFlow.Status()) == EFlow_READY && iSendQ.Remove(packet))
+		{
+		info = packet.PeekInfo();
+		info->iFlow.Open(iFlow);
+		Protocol()->Send(packet);
+		LOG(count++);
+		}
+
+	// Local congestion control
+	if (flowStatus == EFlow_HOLD && !iSendQ.IsEmpty())
+		SourceQuench();
+
+	LOG(Log::Printf(_L("\ttcp SAP[%u] %d segments transmitted"), (TInt)this, count));
+	LOG(if (flowStatus > EFlow_READY) Log::Printf(_L("\ttcp SAP[%u] Flow BLOCKED"), (TInt)this));
+	//LOG(if (flowStatus != EFlow_READY) Log::Printf(_L("CProviderTCP6::Transmit(): Flow status = %d.\r\n"), flowStatus));
+
+	if (flowStatus < 0)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Error %d. Segments"), (TInt)this, flowStatus));
+		ErrorExpire(flowStatus);
+		}
+	}
+
+
+// NOTE: There are a few tricks in the ECN implementation that should be considered. ECT bit MUST NOT
+// be set in IP packets in the following cases [RFC 3168, Sec. 6.1.4 -- 6.1.6]:
+// 1) pure ACKs
+// 2) retransmitted segments
+// 3) zero window probes
+
+
+//
+// This routine generates and transmits a TCP segment.
+//
+TInt CProviderTCP6::SendSegment(TUint8 aFlags, TTcpSeqNum aSeq, TUint32 aDataLen)
+	{
+	//LOG(Log::Printf(_L("CProviderTCP6::SendSegment(%06b,%d,%d)\r\n"),aFlags,aSeq,aMaxLen));
+
+	ASSERT(!(aFlags & KTcpCtlRST));
+
+	RMBufSendPacket seg;
+	RMBufSendInfo *info = NULL;
+	TInt err;
+	TInt up = iSND.UP - aSeq;
+	TInt seqLen = aDataLen;
+	if (aFlags & (KTcpCtlSYN|KTcpCtlFIN))
+	    ++seqLen;
+
+	if (iFlags.iEcnHaveCongestion)
+		{
+		aFlags |= KTcpCtlECE;
+		}
+
+	if (iFlags.iEcnSendCWR)
+		{
+		aFlags |= KTcpCtlCWR;
+		iFlags.iEcnSendCWR = EFalse;
+		}
+
+	//
+	// Don't output SACK blocks with data segments
+	//
+	iOptions.SuppressSack(aDataLen > 0);
+	TUint headerLen = KTcpMinHeaderLength + iOptions.Length();
+
+	//
+	// Allocate memory for TCP segment and IP headers.
+	//
+	// Important: we assume that, when using the TimeStamps option,
+	// iOptions always contains timestamps, so that the header length
+	// does not change when we update them below.
+	//
+	for (;;)
+		{
+		TInt hdrReserve = iFlow.FlowContext()->HeaderSize() + headerLen;
+		if (aDataLen)
+			{
+			err = iSockOutQ.Copy(seg, aSeq - iSND.UNA, aDataLen, hdrReserve);
+			if(err == KErrNone)
+				{
+				err = seg.Prepend(hdrReserve, iBufAllocator);
+				}
+			}
+		else
+			{
+			err = seg.Alloc(hdrReserve, iBufAllocator);
+			}
+		if(err == KErrNone)
+			{
+			info = seg.NewInfo();
+			if(info)
+				{
+				break;
+				}
+			else
+				{
+				err = KErrNoMBufs;
+				}
+			}
+
+		// Allocation failed. Try to recover.
+		seg.Free();
+
+		// Try to free up some memory. XXX - We could do better here.
+		if (!iFragQ.IsEmpty())
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] SendSegment(): RENEGE!!!"), (TInt)this));
+			iFragQ.Free();
+			continue;
+			}
+
+		// If we can't scrounge up a buffer, the packet will be dropped.
+		LOG(Log::Printf(_L("\ttcp SAP[%u] SendSegment(): No memory, packet DROPPED!"), (TInt)this));
+		break;
+		}
+
+	if (err == KErrNone)
+		{
+		// Reserve space for IP headers
+		seg.TrimStart(iFlow.FlowContext()->HeaderSize());
+
+		// Check for urgent data
+		if (up > 0 && up < 0x10000)
+			aFlags |= KTcpCtlURG;
+		else
+			up = 0;
+
+		//
+		// Fill in TCP header. Note that the header length has already
+		// been set and the checksum will be calculated later.
+		//
+		// Note: If we're probing or the receiver has shrunk its advertised
+		// window our iSND.NXT may be pointing beyond its advertised window.
+		// If the packet we're trying to send does not occupy any sequence
+		// space, we will adjust the sequence number so that it falls withín
+		// the receiver's window. This will make sure that our ACK and RST
+		// packets will be accepted. In the worst case, a buggy receiver
+		// might still ignore our window updates, which might lead to a probe
+		// deadlock. Well, a TCP that shrinks its window without adjusting
+		// SND.WL1 deserves what it gets...
+		//
+		TTcpPacket pkt(seg);
+		TTcpSeqNum windowEdge = iSND.UNA + iSND.WND;
+		pkt.iHdr->SetSrcPort(iFlow.FlowContext()->LocalPort());
+		pkt.iHdr->SetDstPort(iFlow.FlowContext()->RemotePort());
+		pkt.iHdr->SetSequence(seqLen > 0 || aSeq <= windowEdge ? aSeq : windowEdge);
+		pkt.iHdr->SetAcknowledgment((aFlags & KTcpCtlACK) ? iRCV.NXT.Uint32() : 0);
+		if (aFlags & KTcpCtlSYN)
+			{
+			// Window scale must not be used in SYN or SYN-ACK segments.
+			// We do not truncate window size to be divisible by MSS.
+			// The next window advertisments are properly aligned, though.
+			pkt.iHdr->SetWindow(Min(iRCV.WND, 0xffff));
+			}
+		else
+			{
+			pkt.iHdr->SetWindow(iRCV.WND >> iRcvWscale);
+			}
+		pkt.iHdr->SetUrgent(up);
+		pkt.iHdr->SetControl(aFlags);
+
+		//
+		// Fill in info struct
+		//
+		// coverity[dead_error_condition]
+		// ASSERT statement required here as the condition "info != NULL" could be false
+		ASSERT(info != NULL);
+		info->iProtocol = KProtocolInetTcp;
+		info->iSrcAddr  = iFlow.FlowContext()->LocalAddr();
+		info->iDstAddr  = iFlow.FlowContext()->RemoteAddr();
+		info->iLength   = headerLen + aDataLen;
+		info->iFlags    = iFlags.iDoPMTUD ? KIpDontFragment : 0;
+
+		// Check if we have to clear the ECN ECT flag because of the few exception cases listed above.
+		if (iFlags.iEcn && (aSeq < iSND.NXT || aDataLen < 1))
+			{
+			info->iFlags |= KIpNoEcnEct;
+			}
+
+		//
+		// Output TCP options (except for RST packets)
+		//
+		if (!(aFlags & KTcpCtlRST))
+			{
+			// Get current time
+			TUint32 usec = TimeStamp();
+
+			//
+			// Measure RTT
+			//
+			if (iFlags.iUseTimeStamps)
+				{
+#if 0
+				// XXX - This code can cause tsVal to go backwards.
+				TUint32 lastSent, lastEchoed;
+				iOptions.TimeStamps(lastSent, lastEchoed);
+				if (usec == lastSent)
+					usec++;
+#endif
+				iOptions.SetTimeStamps(usec, iTsRecent);
+				}
+			if (iFlags.iTiming)
+				{
+				// Check for retransmission. We shouldn't do RTT measurements on
+				// retransmitted segments because of ambiguity (Karn algorithm).
+				if (iTimingSeq > aSeq && iTimingSeq <= aSeq + aDataLen)
+					iFlags.iTiming = EFalse;
+				}
+			else if (aSeq == iSND.NXT && seqLen > 0 && iSND.WND > 0)
+				{
+				// New segment and not a probe. Start timing.
+				iFlags.iTiming = ETrue;
+				iTimingSeq = aSeq + aDataLen;
+				iTimeStamp = usec;
+				}
+
+			//
+			// Output TCP options into the segment header.
+			//
+			pkt.iHdr->SetOptions(iOptions);
+			}
+
+		//
+		// Compute checksum and send the segment.
+		//
+		pkt.ComputeChecksum(seg, info);
+		LOG(CProtocolTCP6::LogPacket('>', seg, info));
+		seg.Pack();
+
+       if(!iFlags.iFastRetransMode)
+           {
+           iSendQ.Append(seg);
+           }
+       else
+           {
+           //If the fast retransmission mode is set then add the segment in the begining of the sendqueue.
+           iSendQ.Prepend(seg);
+           }
+		iSendQ.Wake();
+		}
+
+	CancelDelayACK();
+
+	// Schedule a retransmission. Restart RTO if there was no outstanding segments, otherwise
+	// it is only restarted if it was not running already.
+	if (seqLen)
+		{
+		if (iSND.NXT == iSND.UNA)
+			ReSchedRetransmit();
+		else
+			SchedRetransmit();
+		}
+
+	aSeq += seqLen;
+	if (aSeq > iSND.NXT)
+		iSND.NXT = aSeq;
+	iLastAck = iRCV.NXT;
+	iAdvertisedWindow = iRCV.WND;
+
+	return aDataLen;
+	}
+
+//
+// Transmit one data segment at given position. Use current
+// MTU, send window, and congestion window to limit segment
+// size. Return number of bytes advanced in the transmit queue.
+// This may be less than transmitted bytes if SACK is being used.
+//
+TInt CProviderTCP6::SendDataSegment(TTcpSeqNum aSeq, TBool aNagleOverride)
+	{
+	TInt effMSS = EffectiveMSS();
+#ifdef SIGNED_UNSIGNED_FIX //if user app set the value which is bigger than 0x7ffffff,The largest possible value for a TInt16.
+	TUint effWND = MinUU(iSND.WND, iSockOutQLen); 
+#else
+	TUint effWND = Min(iSND.WND, iSockOutQLen);
+#endif
+	TTcpSeqNum seq = aSeq;
+	TUint8 flags = KTcpCtlACK;
+	TInt len;
+
+	// If RTO just occurred and F-RTO processing is underway, override Nagle.
+	if (iFRTOsent)
+		{
+		aNagleOverride = ETrue;
+		}
+
+	//
+	// Transmission guided by SACK (RFC2018)
+	//
+	if (iFlags.iSackOk)
+		{
+		TTcpSeqNum limitSeq, limit;
+		TInt awnd, sacked;
+
+		// Find a slot between SACKed blocks
+		sacked = iSacked.FindGap(seq, limit);
+
+		if (iFlags.iFastRetransMode)
+			{
+			// Apply the FACK algorithm
+			TTcpSeqNum fack = iSacked.Count() ? iSacked.Last()->iRight : iSND.UNA;
+			awnd = iCwnd - (iSND.NXT - fack);
+			//
+			// Allow at least one segment if there are no retransmits out.
+			// This combines new-Reno style retransmit behaviour with the
+			// FACK algorithm. Pure FACK is somewhat tardy sending out the
+			// first retransmit, which causes problems in the usual single
+			// segment loss case.
+			//
+			if (iRetranData)
+				awnd -= iRetranData;
+			else if (awnd < (TInt)iSMSS)
+				awnd = iSMSS;
+			limitSeq = iSND.UNA + effWND;
+			LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): FAST RECOVERY: una=%u fack=%u awnd=%d"), (TInt)this, (iSND.UNA).Uint32(), fack.Uint32(), awnd));
+			}
+		else
+			{
+			//
+			// Apply congestion window and limited transmit window
+			//
+			// Note: if there are SACKed blocks within the congestion window,
+			// the window is extended accordingly. This is compatible with
+			// the congestion control principles, since we will not be
+			// retransmitting the SACKed blocks.
+			//
+#ifdef SIGNED_UNSIGNED_FIX
+			awnd = (TInt)MinUU(effWND, iCwnd + iLwnd + (TUint)Max(sacked, 0));
+#else
+			awnd = Min(effWND, iCwnd + iLwnd + (sacked < 0 ? 0 : sacked));
+#endif
+			limitSeq = iSND.UNA + awnd;
+			LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): NORMAL MODE: una=%u limit=%u wnd=%u"), (TInt)this, (iSND.UNA).Uint32(), limitSeq.Uint32(), awnd));
+			}
+
+		if (sacked >= 0)
+			{
+			if (limit < limitSeq)
+				{
+				LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): SACK Adjust: RIGHT %u -> %u"), (TInt)this, limitSeq.Uint32(), limit.Uint32()));
+				limitSeq = limit;
+				if (limit <= seq + awnd)
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): NAGLE override (SACK)"), (TInt)this));
+					aNagleOverride = ETrue;
+					}
+				}
+			}
+		else if (iFlags.iFastRetransMode)
+			{
+			//
+			// We are beyond the last SACK block. Skip to new data.
+			//
+			LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): SACK Adjust: SEND NEW DATA"), (TInt)this));
+			iSendHigh = seq;  // Store position of SACK transmit
+			seq = iSND.NXT;   // Start transmitting new data
+			}
+
+		LOG(if (seq > aSeq) Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): SACK Adjust: LEFT %u -> %u"), (TInt)this, aSeq.Uint32(), seq.Uint32()));
+#ifdef SIGNED_UNSIGNED_FIX
+		if(limitSeq > seq)
+			{
+			len = (TInt)MinUS(limitSeq - seq, awnd);
+			}
+		else
+			{
+			len = Min(limitSeq - seq, awnd);
+			}
+#else
+		len = Min(limitSeq - seq, awnd);
+#endif
+		}
+	else
+		{
+#ifdef SIGNED_UNSIGNED_FIX
+		len = iSND.UNA + MinUU(effWND, iCwnd + iLwnd) - seq;
+#else
+		len = iSND.UNA + Min(effWND, iCwnd + iLwnd) - seq;
+#endif
+		}
+	LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(%u, %d): una=%u nxt=%u off=%d noff=%d len=%d"),
+		(TInt)this, aSeq.Uint32(), aNagleOverride, (iSND.UNA).Uint32(), (iSND.NXT).Uint32(), aSeq - iSND.UNA, seq - iSND.UNA, len));
+	LOG(Log::Printf(_L("\ttcp SAP[%u] Transmit state:  effMSS=%d iCwnd=%d iLwnd=%d effWND=%d iRetranData=%d recovery=%d"),
+		(TInt)this, effMSS, iCwnd, iLwnd, effWND, iRetranData, iFlags.iFastRetransMode));
+
+	//
+	// Sender side SWS avoidance and Nagle.
+	//
+	if (len <= 0)
+		len = 0;
+	else if (len >= effMSS)
+		len = effMSS;
+	else if (seq > iSND.NXT && seq + len < iSND.UNA + iSockOutQLen && !aNagleOverride)
+		//
+	// Nagle override is not on and we're trying to send a partial
+	// segment from the middle of the send queue. Therefore, we must
+	// be constrained by the receiver window or the congestion window.
+	//
+	// If receiver window is very small we must allow a small packet out.
+	//
+	len = 0;
+	else
+		{
+		//
+		// Minshall's modification to the Nagle algorithm. We are
+		// allowed to transmit a partial segment if there are no
+		// other unacknowledged partial segments in flight.
+		//
+		if (iPartialSeq <= iSND.UNA && !Protocol()->StrictNagle() && !iFlags.iCork)
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): NAGLE override (Minshall), seq=%u"), (TInt)this, seq.Uint32()));
+			aNagleOverride = ETrue;
+			iPartialSeq = seq + len;
+			}
+
+		//
+		// Standard Nagle algorithm. Do not send partial packets
+		// if there are outstanding unacknowledged segments.
+		// We also refrain from sending small packets if we have
+		// blocked the application as that means our send buffer
+		// is full.
+		//
+		// Update: With Cork option enabled, only full-sized segments are sent
+		//
+		// Note that we temporarily disable Nagle if we have
+		// some urgent data to send or if the user has already
+		// closed the outgoing direction of the connection.
+		//
+		if((iSockFlags.iFlowStopped || iSND.UNA < seq || iFlags.iCork)
+			&& iSND.UP <= seq && !iFlags.iNoDelay
+			&& !iSockFlags.iSendClose && !aNagleOverride)
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] SendDataSegment(): NAGLE kicked in"), (TInt)this));
+			len = 0;
+			}
+		else
+			flags |= KTcpCtlPSH;  // Set PSH on all partial segments
+		}
+
+	if (len)
+		{
+		if (seq + len == iSND.UNA + iSockOutQLen)
+			{
+			// Send queue has drained. Set PSH if application is not blocked.
+			if (!iSockFlags.iFlowStopped)
+				flags |= KTcpCtlPSH;
+
+			// Set FIN if the socket is closing.
+			if (iSockFlags.iSendClose)
+				{
+				flags |= KTcpCtlFIN;
+				if (InState(ETcpEstablished|ETcpCloseWait))
+					EnterState(InState(ETcpEstablished) ? ETcpFinWait1 : ETcpLastAck);
+				}
+			}
+
+		if (len = SendSegment(flags, seq, len), len >= 0)
+			{
+			// Count retransmitted data for FACK
+			if (iSacked.Count() && seq < iSacked.Last()->iRight)
+				{
+				iSendHigh = seq + len;
+				if (iFlags.iFastRetransMode)
+					iRetranData += len;
+				}
+
+			// Take SACK advance into account
+			len += (seq - aSeq);
+			}
+		}
+	else if (iSND.WND < (TUint)effMSS)
+		SchedRetransmit(); // Start probing
+
+	//
+	// Return the number of bytes by which to advance transmit sequence.
+	// Note: With SACK this is not necessarily the number of bytes sent.
+	//
+	return len;
+	}
+
+
+//
+// Send multiple data segments.
+//
+void CProviderTCP6::SendSegments(TBool aNagleOverride)
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] SendSegments(): queue=%u wnd=%u cwnd=%u, ssthresh=%u"),
+		(TInt)this, iSockOutQLen, iSND.WND, iCwnd, iSsthresh));
+
+	iFlags.iTransmitPending = (iFlow.Status() == EFlow_PENDING);
+	if (iFlags.iTransmitPending)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] SendSegments(): Flow pending"), (TInt)this));
+		return;
+		}
+
+	if (InState(ETcpSynReceived|ETcpEstablished|ETcpCloseWait|ETcpLastAck|ETcpFinWait1|ETcpClosing))
+		{
+		TInt advance;
+		TUint count = 0;
+
+		if (iTransmitSeq < iSND.UNA)
+			iTransmitSeq = iSND.UNA;
+
+		// Transmit segments from queue
+		while (advance = SendDataSegment(iTransmitSeq, aNagleOverride), advance > 0)
+			{
+			iTransmitSeq += advance;
+			//
+			// Limit transmission to KTcpMaxTransmit segments during
+			// fast retransmit/recovery.
+			//
+			if (iFlags.iFastRetransMode && ++count >= Protocol()->MaxBurst())
+				break;
+			}
+
+		//
+		// User has closed the connection and send queue has drained?
+		//
+		// Note: Normally the FIN bit is slapped on the last data segment
+		// in SendDataSegment(). However, if we have to send a FIN segement
+		// with no data, we do it here.
+		//
+		if (iSockFlags.iSendClose && 
+			((InState(ETcpEstablished|ETcpCloseWait) &&	iSND.NXT == iSND.UNA + iSockOutQLen && advance >= 0) ||
+			(InState(ETcpSynReceived) && iSockFlags.iRecvClose)))
+			{
+			EnterState(InState(ETcpSynReceived|ETcpEstablished) ? ETcpFinWait1 : ETcpLastAck);
+			SendSegment(KTcpCtlFIN|KTcpCtlACK);
+			}
+		}
+	}
+
+
+//
+// Handle retransmission timeout
+//
+// The retransmission timer is shared for multiple purposes.
+// There are different reasons why we might end up here:
+//
+//   1) A normal retransmission timeout. In this case, iSND.NXT > iSND.UNA.
+//   2) We are probing a zero window. In this case iSND.WND == 0.
+//   3) The Nagle override timeout has expired. In this case,
+//      iSND.WND != 0 and unsent < effMSS.
+//   4) TIME-WAIT or FIN-WAIT-2 timeout.
+//   5) If TCP keepalive option is set, expiry of the keepalive timer
+//
+void CProviderTCP6::RetransmitTimeout()
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] RetransmitTimeout(): backoff=%d"), (TInt)this, iBackoff));
+
+	TUint maxRetries = InState(ETcpSynSent|ETcpSynReceived)
+		? Protocol()->SynRetries() : Protocol()->Retries2();
+
+	// Handle backoff and expiration
+	if (iSND.NXT > iSND.UNA || iSND.WND == 0)
+		{
+		//
+		// Exponential backoff.
+		//
+		++iBackoff;
+		if (iRTO < Protocol()->MaxRTO())  // Avoid RTO overflow
+			ResetRTO();
+
+		//
+		// Timeout?
+		//
+		// Note: we time out if this is a connect attempt or a retransmission,
+		// but we must not time out if we're probing a zero window.
+		// Exception: probing timeouts if application has closed and we have zero window
+		//
+		if ((iBackoff > maxRetries) &&
+			(iSND.WND > 0 
+			|| InState(ETcpSynSent|ETcpSynReceived) 
+			|| (iSockFlags.iSendClose && iSockFlags.iRecvClose)))
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] RetransmitTimeout(): TCP timed out"), (TInt)this));
+			ErrorExpire(iLastError.iStatus != KErrNone ? iLastError.iStatus : KErrTimedOut);
+			return;
+			}
+		//
+		// Simple black hole detection for PMTUD. Currently, we never try to re-enable
+		// PMTUD after hitting a black hole.
+		//
+		if (iBackoff > Protocol()->Retries1() && iSND.WND > 0)
+			{
+			iFlags.iDoPMTUD = EFalse;
+			iSMSS = Min(iSMSS, KTcpStandardMSS);
+			}
+		}
+
+	else if (iFlags.iHaveKeepAlive && !iSockOutQLen && InState(ETcpEstablished | ETcpCloseWait))
+		{
+		KeepAliveTimeout();
+		return;
+		}
+
+	// TIME-WAIT or FIN-WAIT-2 timeout
+	if (InState(ETcpTimeWait) || (InState(ETcpFinWait2) && iSockFlags.iRecvClose))
+		{
+		Expire();
+		return;
+		}
+
+	// Not dead yet. Go retransmit some segments.
+	RetransmitSegments();
+	}
+
+
+void CProviderTCP6::KeepAliveTimeout()
+	{
+	// Keepalive timer expired. Because 32-bit microseconds are not enough for the minimum keepalive
+	// timeout of two hours, the keepalive interval needs to be consumed one hour at a time.
+
+	ASSERT(iRetransTimer);
+
+	if (iBackoff >= Protocol()->NumKeepAlives())
+		{
+		// No response from the peer. Terminate connection
+		LOG(Log::Printf(_L("\ttcp SAP[%u] KeepAliveTimeout(): No response to Keep-Alive probes. Closing connection"), (TInt)this));
+		ErrorExpire(KErrTimedOut);
+		return;
+		}
+
+	TUint32 usec = TimeStamp();
+
+	if (!iLastTimeout)
+		iLastTimeout = usec;
+
+	TUint32 distance = (usec - iLastTimeout) / KOneSecondUs;  // seconds
+	TUint32 interval = iBackoff ? Protocol()->KeepAliveRxmt() : Protocol()->KeepAliveIntv();
+
+	if (distance > interval)
+		{
+		// Send a keepalive probe. If no ack arrives, next one is sent after a shorter time (75 s)
+		LOG(Log::Printf(_L("\ttcp SAP[%u] KeepAliveTimeout(): Sending a Keep-Alive probe"), (TInt)this));
+		SendSegment(KTcpCtlACK, iSND.UNA - 1, 0);
+		iBackoff++;
+		iRetransTimer->Restart(Protocol()->KeepAliveRxmt() * KOneSecondUs);
+		}
+	else
+		{
+		// This branch is entered when the first keepalive has to be issued after an idle period.
+		distance = Protocol()->KeepAliveIntv() - distance;
+		iRetransTimer->Restart((distance > 1800) ?
+			1800 * KOneSecondUs : (distance * KOneSecondUs));
+		}
+	}
+
+
+void CProviderTCP6::ResetKeepAlives()
+	{
+	ASSERT(iRetransTimer);
+	iRetransTimer->Restart((Protocol()->KeepAliveIntv() > 1800) ?
+		1800 * KOneSecondUs : (Protocol()->KeepAliveIntv() * KOneSecondUs));
+	// Backoff is used for counting unacknowledged keepalive retransmissions during idle periods
+	iBackoff = 0;
+	iLastTimeout = TimeStamp();
+	}
+
+inline TBool CProviderTCP6::CanTriggerKeepAlive()
+	{ 
+	//
+	// We can only send keep-alive if we're idle and established.
+	//
+	return iFlags.iHaveTriggeredKeepAlive
+		&& !iSockOutQLen 
+		&& iSendQ.IsEmpty()
+		&& InState(ETcpEstablished) 
+		&& iSND.NXT == iSND.UNA 
+		&& iSND.WND > 0;
+	}
+
+//
+// Try to retransmit segments. This routine gets called from two places:
+//  - directly from RetransmitTimeout()
+//  - from CanSend(), in which case this is a delayed retransmission timeout
+//
+void CProviderTCP6::RetransmitSegments()
+	{
+	ASSERT(iRetransTimer);
+
+	LOG(Log::Printf(_L("\ttcp SAP[%u] RetransmitSegments(): queue=%u wnd=%u cwnd=%u, ssthresh=%u dupacks=%d"),
+		(TInt)this, iSockOutQLen, iSND.WND, iCwnd, iSsthresh, iDupAcks));
+
+	TInt unacked = Min(iSND.NXT - iSND.UNA, iSockOutQLen);  // Adjust for FIN
+	TInt effMSS = EffectiveMSS();
+
+	// Delay retransmission if the flow is pending
+	iFlags.iRetransmitPending = (iFlow.Status() == EFlow_PENDING);
+	if (iFlags.iRetransmitPending)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] RetransmitSegments(): Flow pending"), (TInt)this));
+		ReSchedRetransmit();
+		return;
+		}
+
+	//
+	// Handle retransmission of data segments and zero window probing.
+	// We must have something in output queue. SYN or FIN retransmissions
+	// and TIME-WAIT or FIN-WAIT-2 timeouts are handled later.
+	//
+	if (iSockOutQLen && InState(ETcpEstablished|ETcpFinWait1|ETcpClosing|ETcpCloseWait|ETcpLastAck))
+		{
+		if (iSND.WND == 0)
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] RetransmitSegments(): Window PROBE"), (TInt)this));
+
+			//
+			// We are probing a zero window.
+			//
+			switch (Protocol()->ProbeStyle())
+				{
+			case 1:
+				// Probe with a full segment (BSD style)
+				SendSegment(KTcpCtlACK, iSND.UNA, Min(iSockOutQLen - unacked, effMSS));
+				break;
+
+			case 2:
+				//
+				// Probe with a below-window ACK (Linux style).
+				//
+				// Note:   SendSegment() does not allow sending an above-window ACK.
+				//         Below-window ACK is fine, though.
+				//
+				// Note 2: If we have urgent data to send, we fall back on standard probe.
+				//         since a pure ack can not deliver the urgent pointer.
+				//
+				if (iSND.UP <= iSND.UNA)
+					{
+					SendSegment(KTcpCtlACK, iSND.UNA - 1);
+					SchedRetransmit();
+					break;
+					}
+				// Fall through
+
+			default:
+				// Standard probe. We use a single byte to probe a zero window.
+				SendSegment(KTcpCtlACK, iSND.UNA, 1);
+				}
+			//
+			// Note 1: With probes including data iSND.NXT will now be pointing beyond
+			// the advertised window of the receiver! This will allow us to accept the
+			// incoming probe ack without any special hacks. However, it also means
+			// that if we send an ACK or RST, we need to make sure the sequence number
+			// is within the receiver window! SendSegment() now makes this adjustment
+			// as a special case.
+			//
+			// Note 2: We may also end up with iSND.NXT pointing beyond the window if
+			// the receiver suddenly shrinks its window. The current solution covers
+			// both cases.
+			//
+			return;
+			}
+
+		//
+		// This is a retransmit timout. Do we have anything to do?
+		//
+		if (!unacked)
+			return;
+
+		LOG(if (iFlags.iFastRetransMode) Log::Printf(_L("\ttcp SAP[%u] RetransmitSegments(): Leaving FAST RETRANS mode"), (TInt)this));
+		iFlags.iFastRetransMode = EFalse;
+		iDupAcks = 0;
+
+		//
+		// Congestion control
+		//
+		iSsthresh = Max((iSND.NXT - iSND.UNA) / 2, 2 * iSMSS);
+		iCwnd = EffectiveMSS();
+		iLwnd = 0;
+		iRetranData = 0;
+		iPartialSeq = iSND.UNA;
+
+		//
+		// The receiver may have reneged. Clear SACK info.
+		//
+		// Only clear the SACK queue if we have a SACKed block immediately
+		// after iSND.UNA. In this case the sender has clearly reneged.
+		// If the peer does this even once, we will no longer trust it
+		// to retain its above-sequence queue and will revert to the normal
+		// RFC2018 behaviour of clearing all SACK info on retransmit timeout.
+		//
+		if (!iFlags.iSackOk || (iFlags.iPeerHasReneged ||
+			(iSacked.Count() && iSacked.First()->iLeft == iSND.UNA)))
+			{
+			if (iFlags.iSackOk)
+				{
+				LOG(if(!iFlags.iPeerHasReneged) Log::Printf(_L("\ttcp SAP[%u] RetransmitSegments(): Peer reneged"), (TInt)this));
+				iFlags.iPeerHasReneged = ETrue;
+				iSacked.Clear();
+				}
+
+			//
+			// New reno "bugfix" [RFC2582] is applied only if we are forced
+			// to discard the SACK info. If we can keep the info we will
+			// never send spurious retransmits and thus there will be no
+			// dupacks to trigger fast recovery, unless segments have
+			// really been lost.
+			//
+			iSendHigh = iSND.NXT;
+			}
+
+		iRealSendHigh = iSND.NXT;
+
+		// Save timestamp for delay spike detection
+		if (iFlags.iUseTimeStamps)
+			iLastTimeout = TimeStamp();
+
+		//
+		// Retransmit segments.
+		//
+		if (Protocol()->FRTO())
+			{
+			// F-RTO: Send first unacknowledged segment and continue transmitting new data
+			iFRTOsent = 1;
+			SendDataSegment(iSND.UNA, ETrue);
+			}
+		else
+			{
+			// Normal retransmit
+			iTransmitSeq = iSND.UNA;
+			SendSegments(ETrue);
+			}
+
+		// Store retransmit sequence for SACK retransmit
+		if (iTransmitSeq > iSendHigh && !iFRTOsent)
+			iSendHigh = iTransmitSeq;
+
+		// If the server doesn't respond because of broken NAT/FW or other, don't keep interface up.
+		if (InState(ETcpFinWait1|ETcpClosing|ETcpLastAck))
+			DetachIfDead();
+		return;
+		}
+
+	//
+	// Ok, this is either a SYN/FIN retransmit or a TIME-WAIT/FIN-WAIT-2 timeout.
+	//
+	if (InState(ETcpSynSent))
+		{
+		// Retransmit SYN
+		SendSegment(KTcpCtlSYN, iSND.UNA);
+		return;
+		}
+
+	if (InState(ETcpSynReceived))
+		{
+		// Retransmit SYN,ACK
+		SendSegment(KTcpCtlSYN|KTcpCtlACK, iSND.UNA);
+		return;
+		}
+
+	if (InState(ETcpFinWait1|ETcpClosing|ETcpLastAck))
+		{
+		// If the server doesn't respond because of broken NAT/FW or other, don't keep interface up.
+		DetachIfDead();
+		// Retransmit FIN
+		SendSegment(KTcpCtlFIN|KTcpCtlACK, iSND.UNA);
+		return;
+		}
+
+	LOG(Log::Printf(_L("\ttcp SAP[%u] RetransmitSegments(): Retransmitter stopping"), (TInt)this));
+	if (!iSockFlags.iAttached)
+		Expire();
+	return;
+	}
+
+
+//
+// Respond to an explicit congestion control signal.
+// Currently, the signal can come from the link layer
+// or from the network as an ICMP source quench.
+//
+TBool CProviderTCP6::SourceQuench()
+	{
+	//
+	// Allow source quenching approximately once per window.
+	// Note: the test is written in such a way that iQuenchSeq
+	// does not need to be updated during normal TCP operation.
+	//
+	// Do not shrink the congestion window if we're doing fast
+	// retransmits. That would mess up the congestion window
+	// deflation when exiting fast retransmit mode.
+	//
+	if (iQuenchSeq.Outside(iSND.NXT, iSND.NXT + iSND.WND) && !iFlags.iFastRetransMode)
+		{
+		iSsthresh = Max((iSND.NXT - iSND.UNA) / 2, 2 * iSMSS);
+		iCwnd = iSsthresh;
+		iQuenchSeq = iSND.NXT + iSND.WND;
+		LOG(Log::Printf(_L("\ttcp SAP[%u] SourceQuench(): flight=%d, cwnd=%d, ssthresh=%d"),
+			(TInt)this, iSND.NXT - iSND.UNA, iCwnd, iSsthresh));
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+
+TInt CProviderTCP6::SenderCallBack(TAny* aProviderTCP)
+	{
+	LOG(Log::Printf(_L("<>\ttcp SAP[%u] SenderCallBack"), (TInt)aProviderTCP));
+	((CProviderTCP6*)aProviderTCP)->SendSegments();
+	return 0;
+	}
+
+TInt CProviderTCP6::ReceiverCallBack(TAny* aProviderTCP)
+	{
+	LOG(Log::Printf(_L("<>\ttcp SAP[%u] ReceiverCallBack"), (TInt)aProviderTCP));
+	((CProviderTCP6*)aProviderTCP)->ProcessSegments();
+	return 0;
+	}
+
+TInt CProviderTCP6::DelayAckCallBack(TAny* aProviderTCP)
+	{
+	LOG(Log::Printf(_L("<>\ttcp SAP[%u] DelayAckCallBack"), (TInt)aProviderTCP));
+	((CProviderTCP6*)aProviderTCP)->SendSegment(KTcpCtlACK);
+	return 0;
+	}
+
+TInt CProviderTCP6::TransmitterCallBack(TAny* aProviderTCP)
+	{
+	LOG(Log::Printf(_L("<>\ttcp SAP[%u] TransmitterCallBack"), (TInt)aProviderTCP));
+	((CProviderTCP6*)aProviderTCP)->Transmit();
+	return 0;
+	}
+
+TInt CProviderTCP6::RetransmitterCallBack(TAny* aProviderTCP)
+	{
+	LOG(Log::Printf(_L("<>\ttcp SAP[%u] RetransmitterCallback"), (TInt)aProviderTCP));
+	((CProviderTCP6*)aProviderTCP)->RetransmitTimeout();
+	return 0;
+	}
+
+
+TInt CProviderTCP6::LingerTimerCallBack(TAny *aProviderTCP)
+	{
+	LOG(Log::Printf(_L("<>\ttcp SAP[%u] Linger timeout()"), (TInt)aProviderTCP));
+	((CProviderTCP6*)aProviderTCP)->Detach();
+	return 0;
+	}
+
+
+//
+// Initialize SRTT measurement
+//
+void CProviderTCP6::ClearRTT()
+	{
+	iSRTT = 0;
+	iMDEV = Protocol()->MdevSmooth() * Protocol()->InitialRTO();
+	if (Protocol()->RTO_K())
+		iMDEV /= Protocol()->RTO_K();
+	}
+
+
+//
+// Update RTO using van Jacobson's algorithm
+//
+void CProviderTCP6::UpdateRTO(TUint32 aRTT)
+	{
+	if (!iSRTT && aRTT > Protocol()->InitialRTO() / 2)
+		{
+		iSRTT = Protocol()->SrttSmooth() * aRTT;
+		iMDEV = Protocol()->MdevSmooth() * aRTT / 2;
+		}
+	else
+		{
+		TInt delta = aRTT - (iSRTT / Protocol()->SrttSmooth()); // delta >= -iSRTT
+		iSRTT += delta;
+		if (!iSRTT)
+			iSRTT = 1;
+		if (delta < 0)
+			delta = -delta;
+		delta -= (iMDEV / Protocol()->MdevSmooth()); // delta >= -iMDEV
+		iMDEV += delta;
+		}
+
+	ResetRTO();
+	LOG(Log::Printf(_L("\ttcp SAP[%u] UpdateRTO(): RTT=%d SRTT=%d MDEV=%d BACKOFF=%d RTO=%d"),
+		(TInt)this, aRTT, iSRTT / Protocol()->SrttSmooth(), iMDEV / Protocol()->MdevSmooth(), iBackoff, iRTO));
+	}
+
+//
+// Calculate RTO with backoff
+//
+void CProviderTCP6::ResetRTO()
+	{
+	iRTO = (iSRTT / Protocol()->SrttSmooth()) +
+		Max(Protocol()->RTO_K() * iMDEV / Protocol()->MdevSmooth(), Protocol()->RTO_G());
+	if (iRTO < Protocol()->MinRTO())
+		iRTO = Protocol()->MinRTO();
+	iRTO <<= iBackoff;
+	if (iRTO > Protocol()->MaxRTO())
+		iRTO = Protocol()->MaxRTO();
+	LOG(Log::Printf(_L("\ttcp SAP[%u] ResetRTO(): SRTT=%d MDEV=%d BACKOFF=%d RTO=%d"),
+		(TInt)this, iSRTT / Protocol()->SrttSmooth(), iMDEV / Protocol()->MdevSmooth(), iBackoff, iRTO));
+	}
+
+//
+// Set initial congestion window
+//
+void CProviderTCP6::ResetCwnd(TUint aSMSS)
+	{
+	if (Protocol()->RFC2414())
+		iCwnd = Min(4 * aSMSS, Max(2 * aSMSS, 4380));
+	else
+		iCwnd = Protocol()->InitialCwnd() * aSMSS;
+	LOG(Log::Printf(_L("\ttcp SAP[%u] ResetCwnd(): cwnd=%d"), (TInt)this, iCwnd));
+	}
+
+
+//
+// Schedule a FIN-WAIT-2 or TIME-WAIT timeout
+//
+void CProviderTCP6::SchedMsl2Wait()
+	{
+	ASSERT(iRetransTimer);
+
+	iBackoff = 0;
+	iRetransTimer->Restart(Protocol()->Msl2Delay());
+
+	//
+	// Remove this SAP from the user socket count.
+	//
+	DetachFromInterface();
+	}
+
+//
+// Detach the SAP if the application has closed and we seem to keep resending stuff.
+//
+void CProviderTCP6::DetachIfDead()
+	{
+	if (iSockFlags.iRecvClose && iSockFlags.iSendClose && Protocol()->FinPersistency()
+		&& iBackoff >= Protocol()->FinPersistency())
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] Peer looks dead while %d bytes unacked data. Detach!"), 
+			(TInt)this, Min(iSND.NXT - iSND.UNA, iSockOutQLen)));
+		DetachFromInterface();
+		}
+	}
+
+void CProviderTCP6::DetachFromInterface()
+/**
+Removes this SAP from the user socket count and interface flow count.
+This allows link layer and TCP/IP stack go down if no one else uses it.
+*/
+	{
+	const TInt off = 0;
+	SetOption(KSolInetIp, KSoUserSocket, TPtr8((TUint8*)&off, sizeof(TInt), sizeof(TInt)));
+	SetOption(KSolInetIp, KSoKeepInterfaceUp, TPtr8((TUint8*)&off, sizeof(TInt), sizeof(TInt)));
+	}
+
+
+//
+// Check ioctl completion
+//
+void CProviderTCP6::CompleteIoctl(TInt aError)
+	{
+	if (iFlags.iDataSentIoctl && iSockFlags.iNotify)
+		{
+		if (!iSockOutQLen)
+			{
+			iFlags.iDataSentIoctl = EFalse;
+			iSocket->IoctlComplete(0);
+			}
+		else if (aError != KErrNone)
+			{
+			iFlags.iDataSentIoctl = EFalse;
+			Error(KErrCancel, MSocketNotify::EErrorIoctl);
+			}
+		}
+	}
+
+//
+//  Main incoming segment processing loop
+//
+void CProviderTCP6::ProcessSegments()
+	{
+	LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments()"), (TInt)this));
+
+	ASSERT(iRetransTimer);
+
+	RMBufRecvPacket packet;
+	TBool immediateAck = EFalse;
+
+	while (iRecvQ.Remove(packet))
+		{
+		RMBufRecvInfo* info = packet.Unpack();
+		TIpHeader *ip = ((RMBufPacketPeek &)packet).GetIpHeader();
+		TTcpPacket seg(packet, info->iOffset);
+		TTcpOptions opt;
+		seg.iHdr->Options(opt);
+		TInt len = packet.Length() - seg.iHdr->HeaderLength() - info->iOffset;
+		TTcpSeqNum seq = seg.iHdr->Sequence();
+		TTcpSeqNum ack = seg.iHdr->ACK() ? seg.iHdr->Acknowledgment() : TTcpSeqNum(0);
+		TInt fin = seg.iHdr->FIN();  // Save this for FIN processing
+		CProviderTCP6 *nSAP = NULL;
+		TTcpSeqNum rcvNxt = iRCV.NXT; // Used in SYN_RECEIVED state
+
+		// Get current time
+		TUint32 usec = TimeStamp();
+
+		if (InState(ETcpListen))
+			{
+			TUint32 tsEcr;
+
+			if (!iParent)
+				{
+				//
+				// Server socket LISTEN state processing.
+				//
+
+				// Sanity checks.
+				if (iConnectCount >= iListenQueueSize || seg.iHdr->RST())
+					goto discard;
+
+				// Check invalid address.
+				if (!TInetAddr::Cast(info->iSrcAddr).IsUnicast() ||
+					!TInetAddr::Cast(info->iDstAddr).IsUnicast())
+					goto discard;
+
+				if (IsLandAttack(info))
+					goto discard;
+				
+				if (seg.iHdr->ACK())
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): ACK received in LISTEN state. Sending RST"), (TInt)this));
+					SendReset(ack, info->iDstAddr, info->iSrcAddr);
+					goto discard;
+					}
+
+				if (!seg.iHdr->SYN())
+					goto discard;
+
+				if (opt.Error())
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Invalid TCP options"), (TInt)this));
+					SendResetNoSync(seq+len+1, info->iDstAddr, info->iSrcAddr);
+					goto discard;
+					}
+
+				if (opt.MSS() > 0 && opt.MSS() < STATIC_CAST(TInt, KTcpMinimumMSS))
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Invalid MSS option"), (TInt)this));
+					SendResetNoSync(seq+len+1, info->iDstAddr, info->iSrcAddr);
+					goto discard;
+					}
+
+				//
+				// Sometimes SYN retransmissions get compressed in the network
+				// and get queued in the listen socket. If this happens we just
+				// punt the SYN packet to the correct SAP.
+				//
+#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+				nSAP = (CProviderTCP6*)Protocol()->LocateSap(EMatchConnection, KAFUnspec,
+					info->iDstAddr, info->iSrcAddr);
+#else
+				nSAP = (CProviderTCP6*)Protocol()->LocateSap(EMatchConnection, KAFUnspec,
+					info->iDstAddr, info->iSrcAddr, NULL, info->iInterfaceIndex);
+#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+				if (nSAP == NULL)
+					{
+					// Create a new SAP and initialize it
+					if (CreateChild(nSAP) != KErrNone)
+						goto discard;
+					
+#ifdef SYMBIAN_NETWORKING_UPS					
+					nSAP->iConnectionInfoReceived = 1;
+#endif
+					RFlowContext flowCtxt = nSAP->iFlow;
+					flowCtxt.SetLocalAddr(info->iDstAddr);
+					flowCtxt.SetRemoteAddr(info->iSrcAddr);
+					nSAP->EnterState(ETcpListen);
+
+					// Create a flow context
+					TInt status = flowCtxt.Connect();
+					if (status < KErrNone)
+						{
+						LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSYN(): Error %d opening flow context"), (TInt)this, status));
+						delete nSAP;
+						SendResetNoSync(seq+len+1, info->iDstAddr, info->iSrcAddr);
+						goto discard;
+						}
+
+					// Disable on-demand interface setup for the child
+					flowCtxt.FlowContext()->iInfo.iNoInterfaceUp = 1;
+					nSAP->iSockFlags.iConnected = ETrue;
+					Protocol()->BindProvider(nSAP);
+					}
+
+				// Get the new SAP started. It will handle the rest,
+				packet.Pack();
+				nSAP->Process(packet);
+				continue;
+				}
+
+
+			//
+			// Child socket LISTEN state processing.
+			//
+
+	  /*
+	      Set RCV.NXT to SEG.SEQ+1, IRS is set to SEG.SEQ and any other
+	      control or text should be queued for processing later.  ISS
+	      should be selected and a SYN segment sent of the form:
+
+		<SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
+
+	      SND.NXT is set to ISS+1 and SND.UNA to ISS.  The connection
+	      state should be changed to SYN-RECEIVED.  Note that any other
+	      incoming control or data (combined with SYN) will be processed
+	      in the SYN-RECEIVED state, but processing of SYN and ACK should
+	      not be repeated.  If the listen was not fully specified (i.e.,
+	      the foreign socket was not fully specified), then the
+	      unspecified fields should be filled in now.
+			*/
+
+			// Peer groks timestamps?
+			iFlags.iUseTimeStamps &= opt.TimeStamps(iTsRecent, tsEcr);
+			if (!iFlags.iUseTimeStamps)
+				iOptions.ClearTimeStamps();
+
+			// Peer groks SACK?
+			iFlags.iSackOk &= opt.SackOk();
+			if (!iFlags.iSackOk)
+				iOptions.ClearSackOk();
+
+			// Peer denies ECN
+			if (!seg.iHdr->ECE() || !seg.iHdr->CWR())
+				{
+				iFlags.iEcn = EFalse;
+				}
+
+			// Note: the parent does error checking before instantiating a child socket.
+			if (opt.MSS() >= 0)
+				iSMSS = opt.MSS();
+
+			//
+			// The following is based on network interface MTU.
+			// We limit the advertised MSS to window size divided by four,
+			// in case the interface supportes a large (or infinite) MTU.
+			//
+			iRMSS = Min(LinkRMSS(), iSockInBufSize / 2);
+			iOptions.SetMSS(iRMSS);
+
+			// Initialise send sequence number
+			iTransmitSeq = iSND.NXT = Protocol()->RandomSequence();
+
+			if (!opt.WindowScale() || Protocol()->WinScale() == -1)
+				{
+				iRcvWscale = 0;
+				iOptions.SetWindowScale(0);
+				iSockInBufSize = Min(iSockInBufSize, 0xffff);
+				}
+			else
+				{
+				iSndWscale = opt.WindowScale() - 1;
+				iRcvWscale = (Protocol()->WinScale() > 0 ?
+					Protocol()->WinScale() - 1 : NeedWindowScale());
+				iOptions.SetWindowScale((TUint8)(iRcvWscale + 1));
+				}
+				
+			// Initialize receiver sequence and window
+			iFreeWindow = iSockInBufSize % iRMSS;
+			iRCV.WND = iSockInBufSize - iFreeWindow;
+			iRCV.NXT = seg.iHdr->Sequence() + 1;
+
+			TUint8 flags = KTcpCtlSYN|KTcpCtlACK;
+			if (iFlags.iEcn)
+				{
+				flags |= KTcpCtlECE;
+				}
+
+			// Do not kick idle timers until we are in established state
+			StoreKeepInterfaceUp();
+			iFlow.FlowContext()->SetOption(KSolInetIp, KSoKeepInterfaceUp, KInetOptionDisable);
+
+			// Send SYN-ACK
+			iSND.UNA = iSND.NXT;
+			SendSegment(flags);
+			++iTransmitSeq;
+
+			// Make sure we don't miss our window update.
+			iSND.WL1 = iRCV.NXT;
+			iSND.WL2 = iSND.NXT;
+
+			iPartialSeq = iSND.UNA;
+
+			EnterState(ETcpSynReceived);
+			goto accept;
+			}
+
+		if (InState(ETcpSynSent))
+			{
+			if (seg.iHdr->ACK() && ack.Outside(iSND.UNA, iSND.NXT))
+				{
+				LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Invalid ack=%u"), (TInt)this, ack.Uint32()));
+				if (!seg.iHdr->RST())
+					SendReset(ack);
+				goto discard;
+				}
+
+			if (seg.iHdr->RST())
+				{
+				if (seg.iHdr->ACK())  // No need to check ack sequence because it was tested above
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): RST packet"), (TInt)this));
+					Error(KErrCouldNotConnect, MSocketNotify::EErrorConnect);
+					EnterState(ETcpClosed);
+					goto wrapup;
+					}
+				else
+					goto discard;
+				}
+
+			if (!seg.iHdr->SYN())
+				goto discard;
+
+			// Process options for SYN packet.
+			if (opt.Error())
+				{
+				// In case we receive invalid optons, we should just report an error and reset the connection.
+				LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Invalid options, reset connection"), (TInt)this));
+				if (seg.iHdr->ACK())
+					SendReset(ack);
+				else
+					SendResetNoSync(seq+len+1);
+				Error(KErrCouldNotConnect, MSocketNotify::EErrorConnect);
+				EnterState(ETcpClosed);
+				goto wrapup;
+				}
+
+			if (opt.MSS() > 0)
+				{
+				if (opt.MSS() < STATIC_CAST(TInt, KTcpMinimumMSS))
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Invalid MSS option"), (TInt)this));
+					if (seg.iHdr->ACK())
+						SendReset(ack);
+					else
+						SendResetNoSync(seq+len+1);
+					goto discard;
+					}
+				iSMSS = opt.MSS();
+				}
+
+			iFlags.iUseTimeStamps &= opt.TimeStamps();
+			iFlags.iSackOk &= opt.SackOk();
+
+			if (opt.WindowScale())
+				{
+				iSndWscale = opt.WindowScale() - 1;
+				iFreeWindow = iSockInBufSize % iRMSS;
+				iRCV.WND = iSockInBufSize - iFreeWindow;
+				}
+			else
+				{
+				// iFreeWindow and iRCV.WND were confirmed to be less than 0xffff with SYN
+				iRcvWscale = 0;
+				iSockInBufSize = Min(iSockInBufSize, 0xffff);
+				}
+
+			iRCV.NXT = seq+1;
+
+			// Our SYN was acked?
+			if (seg.iHdr->ACK() && ack == iSND.NXT)
+				{
+				iSND.UNA = ack;
+				iPartialSeq = ack;
+
+				TUint32 tsEcr;
+				// Check that the timestamp echo has sane values
+				if (iFlags.iUseTimeStamps && opt.TimeStamps(iTsRecent, tsEcr)
+						&& tsEcr && usec > tsEcr && usec - tsEcr <= Protocol()->MaxRTO())
+					UpdateRTO(usec - tsEcr);
+				else if (iFlags.iTiming)
+					UpdateRTO(usec - iTimeStamp);
+				iFlags.iTiming = EFalse;
+
+				//
+				// Initial window update as per RFC1122.
+				//
+				//LOG(Log::Printf(_L("CProviderTCP6::ProcessSegments(): Window update.\r\n")));
+				iSND.WND = seg.iHdr->Window(); // no scaling in SYN
+				iSND.WL1 = seq;
+				iSND.WL2 = ack;
+				iLastWnd = seg.iHdr->Window(); // no scaling in SYN
+
+				ResetCwnd(iSMSS);
+				iSsthresh = KMaxTInt;
+
+				ClearSYNSettings();
+
+				// Peer denies ECN with SYN-ACK.
+				if (!seg.iHdr->ECE())
+					{
+					iFlags.iEcn = EFalse;
+					}
+
+				ReadDestinationCache();
+
+				// Open up the window for sender
+				SendSegment(KTcpCtlACK);
+				EnterState(ETcpEstablished);
+				//__ASSERT_DEBUG(iSockFlags.iNotify, User::Panic(_L("notifier"),0));
+				// Assert removed. There's a rare case when the stack is unloading. -MikaL
+
+				// if ECN negotiation was succesful, tell IP layer (iface.cpp) that ECT bit can be enabled.
+				if (iFlags.iEcn)
+					{
+					SetEcn(Protocol()->Ecn());
+					}
+
+				if (iSockFlags.iNotify)
+					iSocket->ConnectComplete();
+					
+				// SYN uses one slot in sequence number space. Account for it before processing data.
+				++seq;
+					
+				goto process_data;
+				}
+
+			//
+			// Simultaneous open, send SYN+ACK
+			//
+			--iSND.NXT;
+
+			// Peer denies ECN in SYN
+			if (!seg.iHdr->ECE() || !seg.iHdr->CWR())
+				{
+				iFlags.iEcn = EFalse;
+				}
+
+			TUint8 flags = KTcpCtlSYN|KTcpCtlACK;
+			if (iFlags.iEcn)
+				{
+				flags |= KTcpCtlECE;
+				}
+			SendSegment(flags);
+			EnterState(ETcpSynReceived);
+			//
+			// According to RFC793 we should queue any additional data
+			// and controls (e.g. URG) for processing once the established
+			// state has been reached.
+			//
+			// No current TCP implementations actually do this (aside from
+			// T/TCP), because of its vulnerability to DoS attacks. In any
+			// case, we're compatible with a peer that wants to send data with
+			// the SYN packet, since the peer will retransmit the data after
+			// we only ack the SYN.
+			//
+			goto accept;
+			}
+
+		if (!InState(ETcpSynReceived|ETcpEstablished|ETcpFinWait1|ETcpFinWait2|
+			ETcpCloseWait|ETcpClosing|ETcpLastAck|ETcpTimeWait))
+			{
+			if (InState(ETcpClosed))
+				{
+				// We have extra segments after entering CLOSED state. Send RST.
+				if (seg.iHdr->ACK())
+					SendReset(ack, info->iDstAddr, info->iSrcAddr);
+				else
+					SendResetNoSync(seq+len, info->iDstAddr, info->iSrcAddr);
+				}
+ 			goto wrapup;
+			}
+
+/*
+      Four cases From RFC793 to test in order to determine whether
+      an arriving segment is acceptable:
+
+      Segment Receive  Test
+      Length  Window
+      ------- -------  -------------------------------------------
+	 0       0     SEG.SEQ = RCV.NXT
+	 0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
+	>0       0     not acceptable
+	>0      >0     RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
+		    or RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
+
+      We transform these into two basic tests, both of which must evaluate
+      true in order to DISCARD a segment. The first test clears the majority
+      of segments that are correctly within receive window. The second test
+      clears special cases where receive window is zero or the incoming segment
+      is only partially within receive window.
+
+      Note that we accept a segment that is straddling iRCV.NXT even if
+      the receive window is zero. In this case the segment is discarded
+      later during data segment processing.
+		*/
+
+		// Eliminate earlier DSACK blocks. One duplicate segment can be reported only once.
+		if (iFlags.iSackOk && Protocol()->DSack())
+			iOptions.SackBlocks().Prune(iRCV.NXT);
+
+		if (InState(ETcpSynReceived))
+			{
+			// In case of simultanous connections TCP retransmits SYN with initial sequence
+			// number when it gets incoming SYN in SYN_SENT state
+			// (it acks received SYN+1 in that case).
+			// Since RCV.NXT is increased after receiving the first SYN, the SYN retransmission
+			// would appear as out-of-order segment, in which case it would be ignored and the TCP
+			// session would never get to ESTABLISHED state.
+			//
+			// This adjustment is meant to avoid the above problem while causing minimal compromise
+			// to TCP sequence number check safety.
+			rcvNxt = iRCV.NXT - 1;
+			}
+
+		//      if ((seq < iRCV.NXT || seq >= iRCV.NXT + iRCV.WND) &&                          // 1st test
+		//	  ((iRCV.WND > 0) ? (seq + len <= iRCV.NXT) : (len > 0 || seq > iRCV.NXT)))  // 2nd test
+		// PeLu: The above test passed case where seq > nxt+wnd
+		if ((seq < rcvNxt || seq >= iRCV.NXT + iRCV.WND) &&                          // 1st test
+			((iRCV.WND > 0) ? ((len > 0) ? (seq+len <= iRCV.NXT) || (seq+len > iRCV.NXT + iRCV.WND) : 1)
+			: ((len > 0) ? 1 : (seq.Outside(rcvNxt, iRCV.NXT)))))
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Segment out of window"), (TInt)this));
+			if (!seg.iHdr->RST())
+				{
+				// if receiving data partially below RCV.NXT,
+				// generate DSACK only for the duplicate portion
+				if (iFlags.iSackOk && Protocol()->DSack() && seq < iRCV.NXT && len > 0)
+					{
+					if (seq + len <= iRCV.NXT)
+						{
+						iOptions.SackBlocks().AddUnordered(seq, seq + len);
+						}
+					else
+						{
+						iOptions.SackBlocks().AddUnordered(seq, iRCV.NXT);
+						}
+					iOptions.SackBlocks().Limit(4);
+					}
+				SendSegment(KTcpCtlACK);
+				}
+			if (InState(ETcpTimeWait|ETcpFinWait2))
+				SchedMsl2Wait();
+			// Accept RST segments that fit within the last advertised window.
+			if (seg.iHdr->RST() && !seq.Outside(iLastAck, iLastAck + iRCV.WND))
+				LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Lagged RST"), (TInt)this));
+			else if (iRCV.WND > 0 || iRCV.NXT.Outside(seq, seq+len))
+				goto discard;
+			}
+
+		if (seg.iHdr->RST())
+			{
+			// Ignore RST in TIME-WAIT state to prevent TIME-WAIT assassination
+			if (InState(ETcpTimeWait))
+				goto discard;
+
+			// Stop processing and enter CLOSED state
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): RST packet"), (TInt)this));
+			Error(KErrDisconnected);
+			EnterState(ETcpClosed);
+			goto wrapup;
+			}
+
+		if (seg.iHdr->SYN() && seq >= iRCV.NXT)
+			{
+	  		/*  If the SYN is in the window it is an error, send a reset, any
+	  		outstanding RECEIVEs and SEND should receive "reset" responses,
+ 		 	all segment queues should be flushed, the user should also
+			receive an unsolicited general "connection reset" signal, enter
+			the CLOSED state, delete the TCB, and return.
+				
+			Note: the extra test against RCV.NXT is there to let through a SYN-ACK
+			with the sequence number RCV.NXT-1. This may happen in a simultaneous
+			connect case, where the SYN-ACK packet is retransmitted. The SYN-ACK
+			will be hanled below as part of SYN-RECEIVED state processing.
+
+			Otherwise, if the SYN is not in the window this step would not be
+			reached and an ack would have been sent in the first step (sequence
+			number check).
+			*/
+			SendReset(iSND.NXT);
+			Error(KErrDisconnected);
+			EnterState(ETcpClosed);
+			goto wrapup;
+			}
+
+
+		//
+		//  ACK PROCESSING
+		//
+		if (!seg.iHdr->ACK())
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): No ACK"), (TInt)this));
+			goto discard;
+			}
+
+		// Check options
+		if (opt.Error())
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Invalid options"), (TInt)this));
+			SendReset(ack);
+			goto discard;
+			}
+
+		if (InState(ETcpSynReceived))
+			if(ack.Inside(iSND.UNA, iSND.NXT))
+			{
+			EnterState(ETcpEstablished);
+
+			const TInt ifup = iFlags.iKeepInterfaceUp;
+			SetOption(KSolInetIp, KSoKeepInterfaceUp, TPtr8((TUint8*)&ifup, sizeof(TInt), sizeof(TInt)));
+
+			// if ECN negotiation was succesful, tell IP layer that ECT bit can be enabled.
+			if (iFlags.iEcn)
+				{
+				SetEcn(Protocol()->Ecn());
+				}
+
+			//
+			// Initial window update as per RFC1122.
+			//
+			//LOG(Log::Printf(_L("CProviderTCP6::ProcessSegments(): Window update.\r\n")));
+			iSND.WND = seg.iHdr->Window() << iSndWscale;
+			iSND.WL1 = seq;
+			iSND.WL2 = ack;
+			iLastWnd = seg.iHdr->Window() << iSndWscale;
+
+			ResetCwnd(iSMSS);
+			iCwnd -= iSMSS;   // Compensate for cwnd increase below during ack processing
+			iSsthresh = KMaxTInt;
+
+			ClearSYNSettings();
+
+			ReadDestinationCache();
+
+			// The following causes the socket server to (eventually) call Start()
+			if (iParent)
+				{
+				iSockFlags.iAttached = ETrue;
+				ASSERT(iParent->iSockFlags.iNotify);
+				if (iParent->CompleteChildConnect(this) == KErrAbort)
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): SAP Deleted, abort ProcessSegments()"), (TInt)this));
+					// Object in use was deleted. Just free the packet and return.
+					packet.Free();
+					return;
+					}
+				}
+			else
+				{
+				//__ASSERT_DEBUG(iSockFlags.iNotify, User::Panic(_L("notifier"), 0));
+				// Assert removed. There's a rare case when the stack is unloading. -MikaL
+				if (iSockFlags.iNotify)
+					iSocket->ConnectComplete();
+				}
+			}
+		else
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Bad SYN-ACK"), (TInt)this));
+			SendReset(ack);
+			goto discard;
+			}
+
+		//
+		// Check ack sequence for all remaining states
+		//
+		if(ack > iSND.NXT)
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): ACK above window"), (TInt)this));
+			SendSegment(KTcpCtlACK);
+			goto discard;
+			}
+
+		// A good packet arrived and the connection is idle, reset the keep-alives state
+		if (CanFireKeepAlives())
+			ResetKeepAlives();
+
+		// Check for ECN congestion established bit.
+		if (info->iVersion == 4)
+			{
+			if (iFlags.iEcn && ip->ip4.EcnIsCongestion())
+				{
+				iFlags.iEcnHaveCongestion = ETrue;
+				LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Have ECN CE bit in IPv4 packet"), (TInt)this));
+				}
+			}
+		else
+			{
+			if (iFlags.iEcn && ip->ip6.EcnIsCongestion())
+				{
+				iFlags.iEcnHaveCongestion = ETrue;
+				LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Have ECN CE bit in IPv6 packet"), (TInt)this));
+				}
+			}
+		
+		if (InState(ETcpEstablished|ETcpFinWait1|ETcpFinWait2|ETcpCloseWait|ETcpClosing|ETcpLastAck))
+			{
+			TInt acked = ack - iSND.UNA;
+
+			if (acked >= 0)
+				{
+				// ECN CWR means that receiver has noticed our Congestion Echo. No more congestion.
+				if (seg.iHdr->CWR())
+					{
+					iFlags.iEcnHaveCongestion = EFalse;
+					}
+
+				if (iFlags.iUseTimeStamps)
+					{
+					TUint32 tsVal, tsEcr;
+					// Check that tsEcr has sane values
+					if (opt.TimeStamps(tsVal, tsEcr) && tsEcr && usec - tsEcr <= KTcpMaxRTO)
+						{
+						if (acked > 0)
+							{
+							UpdateRTO(usec - tsEcr);
+
+							// Got one RTT with timestamps. Don't take another.
+							if (iFlags.iTiming && ack >= iTimingSeq)
+								iFlags.iTiming = EFalse;
+
+							//
+							// Delay spike detection. If the echoed timestamp predates
+							// our last retransmission timeout we abort the retransmission
+							// sequence. However, we do not inflate the congestion window
+							// for two reasons: 1) a long delay might also be a sign of
+							// congestion, and 2) this feature could potentially be used
+							// by a hostile peer to artificially inflate our congestion
+							// window.
+							//
+							// PS: Delay spike detection with timestamps is not in use with F-RTO
+							if (!Protocol()->FRTO() && tsEcr < iLastTimeout &&
+								!opt.SackBlocks().Count())
+								{
+								iTransmitSeq = iSND.NXT;
+								SpuriousTimeout(acked);
+								}
+							}
+
+						//
+						// The condition in RFC1323 is buggy(?):
+						//
+						//    SEG.SEQ <= Last.ACK.sent < SEG.SEQ + SEG.LEN
+						//
+						// The best current practise is the following, because it updates
+						// TSrecent also on pure ACKs:
+						//
+						//    SEG.TSval >= TSrecent and SEG.SEQ <= Last.ACK.sent
+						//
+						if (tsVal > iTsRecent && seq <= iLastAck)
+							iTsRecent = tsVal;
+						}
+					}
+				if (iFlags.iTiming && ack >= iTimingSeq)
+					{
+					UpdateRTO(usec - iTimeStamp);
+					iFlags.iTiming = EFalse;
+					}
+
+				// SACK book keeping
+				if (iFlags.iSackOk)
+					{
+					// Remove acknowledged blocks
+					if (opt.SackBlocks().Count())
+						{
+						SequenceBlockQueueIter iter(opt.SackBlocks());
+						SequenceBlock *block;
+						while (block = iter++, block != NULL)
+							{
+							//
+							// Record SACK block but do some sanity checking first.
+							//
+							if (ack < block->iLeft && block->iLeft < block->iRight
+								&& block->iRight <= iSND.NXT)
+								{
+								iSacked.AddOrdered(block);
+								}
+							}
+
+						// Take RTT estimate using SACK info
+						if (iFlags.iTiming && iSacked.Count() && iSacked.Last()->iRight >= iTimingSeq)
+							{
+							UpdateRTO(usec - iTimeStamp);
+							iFlags.iTiming = EFalse;
+							}
+						}
+					iSacked.Prune(ack);
+
+					// Update iRetranData
+					if (iSacked.Count() && ack < iSendHigh)
+						{
+						iRetranData = iSendHigh - ack;
+						SequenceBlockQueueIter iter(opt.SackBlocks());
+						SequenceBlock *block;
+						while (block = iter++, block != NULL && block->iLeft < iSendHigh)
+							{
+							if (block->iRight < iSendHigh)
+								iRetranData -= (block->iRight - block->iLeft);
+							else
+								iRetranData -= (iSendHigh - block->iLeft);
+							}
+						}
+					else
+						iRetranData = 0;
+
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): acked=%d iRetranData = %d"),
+						(TInt)this, acked, iRetranData));
+					}
+
+				// Did they acknowledge any new data?
+				if (acked > 0)
+					{
+					// Reset retransmit backoff
+					if (iBackoff && iSND.WND > 0)
+						{
+						LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Some data ACKed, clearing Backoff"), (TInt)this));
+						iBackoff = 0;
+						}
+
+					// Trim send queue
+					iSND.UNA = ack;
+					if ((TInt)iSockOutQLen > acked) // Avoid barfing on acked SYN or FIN packet
+						{
+						iSockOutQ.TrimStart(acked);
+						iSockOutQLen -= acked;
+						}
+					else
+						{
+						iSockOutQ.Free();
+						iSockOutQLen = 0;
+						CompleteIoctl(KErrNone);
+						}
+
+					// Tag along
+					if (iSND.UP < ack)
+						iSND.UP = ack - 1;
+
+					// Tag along
+					if (iPartialSeq < ack)
+						iPartialSeq = ack;
+
+					// Tag along
+					if (iSendHigh < ack)
+						iSendHigh = ack - 1;
+
+					if (iFlags.iFastRetransMode)
+						{
+						if (iFlags.iSackOk ? !iSacked.Count() : ack >= iRecoverSeq)
+							{
+							LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Leaving FAST RETRANS mode"), (TInt)this));
+							iFlags.iFastRetransMode = EFalse;
+							iDupAcks = 0;
+							// Deflate congestion window
+							iCwnd = Min(iSsthresh, iSND.NXT - iSND.UNA + iSMSS);
+							}
+						else if (!iFlags.iSackOk)
+							{
+							// NewReno partial ACK processing.
+
+			  /* From RFC2582:
+			   If this ACK does *not* acknowledge all of the data up to and
+			   including "recover", then this is a partial ACK.  In this case,
+			   retransmit the first unacknowledged segment.  Deflate the
+			   congestion window by the amount of new data acknowledged, then
+			   add back one MSS and send a new segment if permitted by the new
+			   value of cwnd.  This "partial window deflation" attempts to
+			   ensure that, when Fast Recovery eventually ends, approximately
+			   ssthresh amount of data will be outstanding in the network.  Do
+			   not exit the Fast Recovery procedure (i.e., if any duplicate ACKs
+			   subsequently arrive, execute Steps 3 and 4 above).
+
+			   For the first partial ACK that arrives during Fast Recovery, also
+			   reset the retransmit timer.
+							*/
+
+							iCwnd -= acked;
+							iCwnd += iSMSS;
+							LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): FAST RETRANSMIT on PARTIAL ACK"), (TInt)this));
+							SendDataSegment(ack);
+
+							//
+							// Socket write makes use of the duplicate ack count to
+							// temporarily extend the send buffer during fast recovery.
+							// We deflate the buffer here.
+							//
+							ASSERT(iSMSS);
+							iDupAcks = Max(iDupAcks - acked / (TInt)iSMSS, 0);
+							}
+						}
+
+					// Reset limited transmit window
+					iLwnd = 0;
+
+					// Everything acked?
+					if (ack == iSND.NXT)
+						{
+						iTransmitSeq = iSND.NXT;
+						}
+					else
+						{
+						// Restart retransmission timeout
+						ReSchedRetransmit();
+						}
+
+					//
+					// Adjust congestion window.
+					//
+					TUint incr = iSMSS;
+
+					if (iCwnd < iSsthresh)
+						iCwnd += incr;			    // Slow-start
+					else
+						iCwnd += Max(incr * incr / iCwnd, 1); // Congestion avoidance
+					}
+
+				else if (ack < iSND.NXT)
+
+					{
+					//
+					// Fast retransmit algorithm.
+					//
+					// Note! We only reset the duplicate ack count if the received
+					// segment acknowledges some new data or if a timeout has
+					// occurred. However, we simply ignore window updates and piggy-back
+					// acknowledgements unless they also acknowledge new data. Other
+					// duplicate acknowledgements increase the duplicate ack count and
+					// may trigger a fast retransmission.
+					//
+					if (len == 0 && (seg.iHdr->Window() << iSndWscale) == iLastWnd)
+						{
+						if (iFlags.iSackOk)
+							{
+							TTcpSeqNum fack = iSacked.Count() ? iSacked.Last()->iRight : iSND.UNA;
+
+							// Acks caused by out-of-window segments don't count
+							if (opt.SackBlocks().Count() && opt.SackBlocks().First()->iRight > iSND.UNA)
+								iDupAcks++;
+
+							 //Removed the conditional checking ack > iSendHigh as it is only being used for if SACK option is not set.
+							 // Set the retransmission mode as the fast retransmit if the three TCP duplicate ACKs are received from the server and
+                             //send the lost segment(s) to the server.
+							if (
+								(iDupAcks >= Protocol()->Reordering() ||
+								fack > iSND.UNA + Protocol()->Reordering() * iRMSS))
+								{
+								LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): SACK RETRANSMIT!"), (TInt)this));
+								iFlags.iFastRetransMode = ETrue;
+								iSsthresh = Max((iSND.NXT - iSND.UNA) / 2, 2 * iSMSS);
+								iCwnd = iSsthresh;
+								iSendHigh = ack;
+								ReSchedRetransmit();    // Restart retransmission timeout
+								iTransmitSeq = iSendHigh; // Rewind transmitter for SACK retransmit
+								SendSegments(ETrue);
+	                            iDupAcks = 0;
+
+								}
+							}
+						else
+							{
+							if (ack > iSendHigh)        // Never retransmitted?
+								iDupAcks++;
+							if (iFlags.iFastRetransMode)
+								{
+								iCwnd += iSMSS;         // Inflate congestion window
+								}
+							else if (iDupAcks == Protocol()->Reordering())
+								{
+								LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): FAST RETRANSMIT!"), (TInt)this));
+								iFlags.iFastRetransMode = ETrue;
+								iSsthresh = Max((iSND.NXT - iSND.UNA) / 2, 2 * iSMSS);
+								iCwnd = iSsthresh + 3 * iSMSS;
+								iRecoverSeq = iSND.NXT;
+								iLwnd = 0;                    // Reset limited transmit window
+								ReSchedRetransmit();	    // Restart retransmission timeout
+								SendDataSegment(ack);         // Retransmit a single segment
+								}
+							}
+						if (!iFlags.iFastRetransMode)
+							{
+							//
+							// Increment limited transmit window
+							//
+							if (iSND.UNA + iCwnd >= iSND.NXT)  // FIXME. Prevent lwnd increase during RTO
+								iLwnd = Min(iLwnd + iSMSS, iSMSS * Protocol()->LtxWindow());
+							ReSchedRetransmit();
+							}
+						}
+
+					}
+				else
+					iDupAcks = 0;
+
+				// F-RTO stuff. If we enter here, RTO has just occurred, and e.g. iDupAcks should be 0
+				if (iFRTOsent)
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] F-RTO: iFRTOsent=%d   acked=%d   ack-sndhigh=%d"),
+						(TInt)this, iFRTOsent, acked, (ack - iSendHigh)));
+
+					// The ack following the RTO did not advance window, or we cannot transmit new
+					// unsent data => conventional recovery
+					//
+					// The last term of the condition below is due to special case:
+					// If the first ack after RTO covers all outstanding data, the RTO was due
+					// to lost retransmit and fixed the whole outstanding window).
+					if (!acked || !CanForwardTransmit() || (ack >= iRealSendHigh && iFRTOsent == 1))
+						{
+
+						LOG(Log::Printf(_L("\ttcp SAP[%u] F-RTO: Doing go-back-N"), (TInt)this));
+						iTransmitSeq = iSND.UNA;
+						if (iFRTOsent == 1 && !acked)
+							{
+							// dupack arrives before RTO retransmission is acknowledged.
+							// don't retransmit the same segment again.
+							iTransmitSeq += EffectiveMSS();
+							}
+						iCwnd = EffectiveMSS() * iFRTOsent; // number of RTTs after RTO
+						iFRTOsent = 0;
+						}
+					else
+						{
+						// Force 2 segments out on first ack after RTO. For that purpose we have to
+						// estimate the current flightsize (-> iCwnd) and set iLwnd to 2.
+						// Note that after this step iCwnd is set either to 2*MSS or iSsthresh, so
+						// the setup below is very temporary.
+						if (iFRTOsent == 1)  // first new ACK after RTO
+							{
+							iCwnd = iSND.NXT - iSND.UNA - iSacked.ByteCount() + 2 * iSMSS;
+							}
+						else if (iFRTOsent == 2)
+							{
+							// Spurious RTO.
+							// second new ACK after RTO, continue in earlier state because the second
+							// ACK was delayed and the RTO was likely spurious.
+							SpuriousTimeout(acked);
+							}
+						// coverity[write_write_order]
+						iFRTOsent = (++iFRTOsent) % 3;
+						}
+					}
+
+				iLastWnd = seg.iHdr->Window() << iSndWscale;
+
+				if (iSND.WL1 < seq || (iSND.WL1 == seq && iSND.WL2 <= ack))
+					{
+					iSND.WND = seg.iHdr->Window() << iSndWscale;
+					iSND.WL1 = seq;
+					iSND.WL2 = ack;
+					//LOG(Log::Printf(_L("CProviderTCP6::ProcessSegments(): Window update: %d.\r\n"), iSND.WND));
+					}
+
+				// if we are not yet in recovery, reduce congestion window on ECN CE Echo.
+				if (iFlags.iEcn && seg.iHdr->ECE())
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Got ECN echo"), (TInt)this));
+
+					// SourceQuench returns False, if congestion window was reduced for some other
+					// reason in the last RTT. However, if it was reduced due to FR
+					// (i.e. last QuenchSeq was more than one RTT ago), we better send CWR to suppress
+					// ECE flags at the other end.
+					if (SourceQuench() || iQuenchSeq.Outside(iSND.NXT, iSND.NXT + iSND.WND))
+						{
+						iFlags.iEcnSendCWR = ETrue;
+						}
+					}
+				if((iSND.NXT - ack) >0 && InState(ETcpEstablished) && (acked ==0))
+					{
+					iRetryAck++;
+					if(iRetryAck >=4) // 4 an arbitary number; as this count does not refer to dup_ack, this will not interfere with Fast retransmission
+						{
+						LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): retransmitting the segment"), (TInt)this));
+						SendSegments(ETrue);
+						iRetryAck = 0; // reset the retry count
+						}
+					}
+					
+				}
+			}
+
+		//
+		// Everything acked? Check if we need to do a state transition.
+		//
+		if (ack == iSND.NXT)
+			{
+			if (InState(ETcpFinWait1))
+				{
+				EnterState(ETcpFinWait2);
+
+				//
+				// If the peer does not send a FIN for some reason,
+				// we might hang in FIN-WAIT-2 indefinitely. We use
+				// a 2*MSL timeout to break out of FIN-WAIT-2.
+				//
+				// Note that we can only do this if the application
+				// has closed both directions of the connection.
+				// However, this should take care of cleanup in the
+				// the most common case where a server socket hangs
+				// in FIN-WAIT-2, because the client has crashed or
+				// disappeared before sending a FIN-ACK.
+				//
+				if (iSockFlags.iRecvClose)
+					{
+					LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Setting FIN-WAIT-2 timeout"), (TInt)this));
+					SchedMsl2Wait();
+					}
+				}
+
+			//
+			// If we have a lingering Close() or Shutdown(ENormal), complete it with KErrNone.
+			//
+			if (iLinger > 0 && InState(ETcpFinWait2|ETcpClosing|ETcpLastAck))
+				{
+				/*
+				In addition to the processing for the ESTABLISHED state, if
+				the retransmission queue is empty, the user's CLOSE can be
+				acknowledged ("ok") but do not delete the TCB.*/
+				iLinger = -1;
+				iLingerTimer->Cancel();
+				Detach();
+				}
+
+			if (InState(ETcpClosing))
+				{
+				EnterState(ETcpTimeWait);
+				}
+
+			if (InState(ETcpLastAck))
+				{
+				if (!Protocol()->LocalTimeWait())
+					{
+					//
+					// Local resource optimization. If the peer is on localhost,
+					// we will terminate it here. Normally, it would wait for
+					// a duration of 2*MSL in TIME-WAIT state before deleting
+					// itself.
+					//
+					CFlowContext *flow = iFlow.FlowContext();
+#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+					CProviderTCP6 *sap =
+						(CProviderTCP6*)Protocol()->LocateSap(EMatchConnection,
+						KAFUnspec,
+						flow->RemoteAddr(),
+						flow->LocalAddr());
+#else
+					CProviderTCP6 *sap =
+						(CProviderTCP6*)Protocol()->LocateSap(EMatchConnection,
+						KAFUnspec,
+						flow->RemoteAddr(),
+						flow->LocalAddr(),
+						NULL, info->iInterfaceIndex);
+#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
+					if (sap != NULL && sap->InState(ETcpTimeWait))
+						{
+						LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Deleting local peer"), (TInt)this));
+						sap->Expire();
+						}
+					}
+				/*
+				The only thing that can arrive in this state is an
+				acknowledgment of our FIN.  If our FIN is now acknowledged,
+				delete the TCB, enter the CLOSED state, and return.*/
+				EnterState(ETcpClosed);
+				goto wrapup;
+				}
+			}
+
+		if (InState(ETcpTimeWait))
+			{
+	  /*
+          The only thing that can arrive in this state is a
+          retransmission of the remote FIN.  Acknowledge it, and restart
+			the 2 MSL timeout.*/
+			//
+			// RFC793 appears to be wrong here. A retransmitted FIN should
+			// already have been acknowledged as an out-of-sequence packet.
+			// Also, a simultaneous close can cause both end points to send
+			// an ack and enter the TIME-WAIT state at the same time. In this
+			// rare case, the following line will cause an ack storm, where
+			// each side is acknowledging ack packets from the other side.
+			//
+			//SendSegment(KTcpCtlACK);
+			SchedMsl2Wait();
+			}
+
+	process_data:
+
+		//
+		//  DATA SEGMENT PROCESSING
+		//
+		if (InState(ETcpEstablished|ETcpFinWait1|ETcpFinWait2))
+			{
+			if (seg.iHdr->URG())
+				{
+				TTcpSeqNum up = seq + seg.iHdr->Urgent();
+				/*
+				If the URG bit is set, RCV.UP <- max(RCV.UP,SEG.UP), and signal
+				the user that the remote side has urgent data if the urgent
+				pointer (RCV.UP) is in advance of the data consumed.  If the
+				user has already been signaled (or is still in the "urgent
+				mode") for this continuous sequence of urgent data, do not
+				signal the user again.*/
+				if (up > seq)
+					RememberUrgentPointer(up);
+				}
+
+			//
+			//  Process data segments.
+			//
+			if (len > 0)
+				{
+				if (iSockFlags.iRecvClose)
+					{
+					// Receive direction has been shut down. Send RST.
+					SendReset(ack);
+					}
+				else
+					{
+					//
+					// Zero window? We will already have sent and ACK in response,
+					// so we can just discard the segment.
+					//
+					if (!iRCV.WND)
+						goto accept;
+
+					TInt maxLen = iRCV.NXT + iRCV.WND - seq;
+					if (len > maxLen)
+						{
+						//
+						// Part of segment is above window -> truncate. This
+						// can happen when peer is probing a zero window.
+						//
+						LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Above window payload: %u"), (TInt)this, len - maxLen));
+						if (maxLen <= 0)  // Sanity check
+							goto accept;
+
+						len = maxLen;
+						packet.TrimEnd(seg.iHdr->HeaderLength() + info->iOffset + len);
+						fin = 0;    // Cannot process FIN yet.
+						}
+
+					// Remember to ack a pushed segment immediately
+					if (seg.iHdr->PSH() && Protocol()->PushAck())
+						immediateAck = ETrue;
+
+					if (seq <= iRCV.NXT)
+						{
+						//
+						// WARNING! This will destroy the segment header!
+						//
+						LOG(if(seq<iRCV.NXT)Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Below window payload: %d"), (TInt)this, iRCV.NXT - seq));
+
+						// Remove segment header and below-window payload.
+						packet.TrimStart(iRCV.NXT - seq + seg.iHdr->HeaderLength() + info->iOffset);
+
+						// Put in receive queue.
+						iSockInQ.Append(packet);
+						len -= (iRCV.NXT - seq);
+						iSockInQLen += len;
+						iNewData += len;
+						iRCV.NXT += len;
+						iRCV.WND -= len;
+
+						//
+						// Check if we can take something out of the reassembly queue.
+						//
+						TTcpSeqNum fragOff;
+						TBool fastAck = !iFragQ.IsEmpty();
+						while (!iFragQ.IsEmpty())
+							{
+							fragOff = iFragQ.First().Offset();
+							if (fragOff > iRCV.NXT)
+								break;
+
+							RMBufTcpFrag frag;
+							iFragQ.Remove(frag);
+							TUint32 fragLen = frag.FragmentLength();
+
+							// Already got this?
+							if (fragOff + fragLen <= iRCV.NXT)
+								{
+								frag.Free();
+								continue;
+								}
+
+							// Ok. Trim it and put it in receive queue.
+							TTcpPacket seg(frag);
+
+							frag.TrimStart(seg.iHdr->HeaderLength() + (iRCV.NXT - fragOff));
+							fragLen -= (iRCV.NXT - fragOff);
+
+							ASSERT(fragLen == (TUint)frag.Length());
+
+							iSockInQ.Append(frag);
+							iSockInQLen += fragLen;
+							iNewData += fragLen;
+
+							ASSERT((TUint)iSockInQLen >= iNewData);
+
+							LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Took %d:%d(%d) from reassembly queue"),
+								(TInt)this, iRCV.NXT.Uint32(), (iRCV.NXT + fragLen).Uint32(), fragLen));
+
+							iRCV.NXT += fragLen;
+							iRCV.WND -= fragLen;
+
+							}
+						//
+						// Update SACK book keeping
+						//
+						if (iFlags.iSackOk)
+							iOptions.SackBlocks().Prune(iRCV.NXT);
+
+						/*
+                      	To provide feedback to senders recovering from losses, the receiver
+                      	SHOULD send an immediate ACK when it receives a data segment that
+						fills in all or part of a gap in the sequence space. */
+						if (fastAck)
+							SendSegment(KTcpCtlACK);
+						}
+					else
+						{
+						LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Processing out-of-order segment!"), (TInt)this));
+						TTcpSeqNum blockSeq;
+						TUint32 blockLen;
+
+						// Fragment queue assumes packet starts from TCP header
+						packet.TrimStart(info->iOffset);
+
+						//packet.FreeInfo(); --  Leave the info. We need it below.
+						RMBufTcpFrag frag;
+						frag.Assign(packet);
+						iFragQ.Add(frag, (TUint32*)&blockSeq, &blockLen);
+						if (iFlags.iSackOk)
+							{
+							iOptions.SackBlocks().AddUnordered(blockSeq, blockSeq + blockLen);
+							iOptions.SackBlocks().Limit(4);
+							}
+
+                      /*
+                      Out-of-order data segments SHOULD be acknowledged immediately, in
+                      order to accelerate loss recovery.  To trigger the fast retransmit
+                      algorithm, the receiver SHOULD send an immediate duplicate ACK when
+						it receives a data segment above a gap in the sequence space. */
+						SendSegment(KTcpCtlACK);
+						}
+					}
+				}
+			}
+
+
+		//
+		//  FIN PROCESSING
+		//
+
+/*
+      If the FIN bit is set, signal the user "connection closing" and
+      return any pending RECEIVEs with same message, advance RCV.NXT
+      over the FIN, and send an acknowledgment for the FIN.  Note that
+      FIN implies PUSH for any segment text not yet delivered to the
+      user.
+		*/
+		//
+		// Remember that we have received a FIN. Note: the FIN may have
+		// been received as part of an out-of-sequence segment, in which
+		// case we may not be ready to process it yet.
+		//
+		if (fin && !iFlags.iFinReceived)
+			{
+			iFlags.iFinReceived = ETrue;
+			iFinSeq = seq + len;
+			}
+
+		//
+		// Process FIN when all data has been received.
+		//
+		if (iFlags.iFinReceived && iRCV.NXT == iFinSeq)
+			{
+			//
+			// Advance iRCV.NXT past the FIN. This also ensures
+			// that we never end up here again.
+			//
+			iRCV.NXT++;
+
+			if (InState(ETcpFinWait1|ETcpFinWait2))
+				{
+				//LOG(Log::Printf(_L("IMMEDIATE FIN ACK\r\n")));
+				SendSegment(KTcpCtlACK);
+				}
+			else
+				{
+				//LOG(Log::Printf(_L("DELAYED FIN ACK\r\n")));
+				SendDelayACK();
+				}
+
+/*
+        SYN-RECEIVED STATE
+        ESTABLISHED STATE
+
+          Enter the CLOSE-WAIT state.
+			*/
+			if (InState(ETcpSynReceived|ETcpEstablished))
+				{
+				EnterState(ETcpCloseWait);
+				}
+
+/*
+        FIN-WAIT-1 STATE
+
+          If our FIN has been ACKed (perhaps in this segment), then
+          enter TIME-WAIT, start the time-wait timer, turn off the other
+          timers; otherwise enter the CLOSING state.
+			*/
+			if (InState(ETcpFinWait1))
+				{
+	      /*
+	      If our FIN has been ACKed (perhaps in this segment), then
+	      enter TIME-WAIT, start the time-wait timer, turn off the other
+				timers; otherwise enter the CLOSING state.*/
+				if (iSND.UNA == iSND.NXT)
+					{
+					EnterState(ETcpTimeWait);
+					SchedMsl2Wait();
+					}
+				else
+					EnterState(ETcpClosing);
+				}
+
+/*
+        FIN-WAIT-2 STATE
+
+          Enter the TIME-WAIT state.  Start the time-wait timer, turn
+          off the other timers.
+			*/
+			if (InState(ETcpFinWait2))
+				{
+	      /*
+	      Enter the TIME-WAIT state.  Start the time-wait timer, turn
+				off the other timers.*/
+				Stop();
+				EnterState(ETcpTimeWait);
+				SchedMsl2Wait();
+				}
+
+			//
+			// Anything that could arrive in TIME-WAIT is an out-of-window
+			// segment and should never end up here.
+			//
+#ifdef notdef
+/*
+        TIME-WAIT STATE
+
+          Remain in the TIME-WAIT state.  Restart the 2 MSL time-wait
+          timeout.
+			*/
+			if (InState(ETcpTimeWait))
+				{
+	      /*
+	      Remain in the TIME-WAIT state.  Restart the 2 MSL time-wait
+				timeout.*/
+				SchedMsl2Wait();
+				}
+#endif
+			}
+
+	wrapup:
+		//
+		// Time to die.
+		//
+		if (InState(ETcpClosed) && iRecvQ.IsEmpty())
+			{
+			packet.Free();
+			Close();
+			Expire();
+			return;
+			}
+
+	accept:
+			// Reset idle timers
+		Protocol()->Interfacer()->PacketAccepted(info->iOriginalIndex);
+
+	discard:
+			packet.Free();
+		}
+
+	// Complete application level read.
+	if (iFlags.iCompleteRecv)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Complete application read"), (TInt)this));
+		iFlags.iCompleteRecv = EFalse;
+		iCopyOutOffset = 0;
+      if (iSockFlags.iNotify)
+		iSocket->Error(KErrNone, MSocketNotify::EErrorRecv);
+		}
+
+	// This may cause an immediate ACK to be sent from within GetData()
+	if (iNewData)
+		{
+		TInt newData = iNewData, up = 0;
+
+		// Adjust for urgent data.
+		if (iUpCount)
+			{
+			//
+			// Find the offset of an urgent byte following a block of
+			// non-urgent data. The number of junked (already delivered)
+			// urgent bytes will be left in <up>. This looks complicated
+			// but should normally be very quick.
+			//
+			newData = iSockInQLen;
+			for (up = 0; up < iUpCount; up++)
+				{
+				TInt offset = UrgentOffset(up);
+				if (offset > up)
+					{
+					newData = Min(offset, iSockInQLen);
+					break;
+					}
+				}
+
+			// We have an undelivered urgent byte within the block.
+			if (iFlags.iUrgentMode && !iFlags.iOobInline && newData > UrgentOffset())
+				newData = UrgentOffset();
+
+			ASSERT(newData <= (TInt)iSockInQLen);
+
+			// Subtract bytes that have already been advertised to ESock
+			newData -= (iSockInQLen - iNewData);
+
+			// Subtract out-of-band bytes
+			if (!iFlags.iOobInline)
+				newData -= up;
+			}
+
+		// Do we have something?
+		if (newData > 0 && iFlags.iStarted && iSockFlags.iNotify)
+			{
+			ASSERT(newData + (iFlags.iOobInline ? 0 : up) <= (TInt)iNewData);
+			iNewData -= newData;
+			if (!iFlags.iOobInline)
+				iNewData -= up;
+
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): NewData(%d), %d urgent bytes %s, %d bytes not reported"),
+				(TInt)this, newData, up, iFlags.iOobInline ? _S("inline") : _S("junked"), iNewData));
+			iPending += newData;
+			iSocket->NewData(newData);
+			}
+		}
+
+	// Notify urgent data to application
+	if (iFlags.iNotifyUrgent && iSockFlags.iNotify)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Urgent data notification"), (TInt)this));
+		iFlags.iNotifyUrgent = EFalse;
+		iSocket->Error(KErrUrgentData, 0);
+		}
+
+	// Notify application that connection has been closed by peer.
+	if (InState(ETcpCloseWait|ETcpClosing|ETcpTimeWait) && !iFlags.iCloseNotified && !iNewData)
+		{
+		iFlags.iCloseNotified = ETrue;
+		if (iFlags.iStarted && iSockFlags.iNotify && !iSockFlags.iRecvClose)
+			{
+			LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): Calling NewData(KNewDataEndOfData)"), (TInt)this));
+			if(iSocket)
+			iSocket->NewData(KNewDataEndofData);
+			}
+		}
+
+	if (iLastAck < iRCV.NXT)
+		{
+		//
+		// We are required to send an ACK for at least every two full sized
+		// segments but at most once every full sized segment.
+		//
+		// In steady state the following rule will acknowledge every second
+		// segment regardless of the options in the segments (unless maximum
+		// segment size is VERY small). However, if we receive segments smaller
+		// than MSS/2, we will send an ACK roughly once for every full segment
+		// of received data. This works out pretty good. However, if the
+		// received segments are significantly smaller, we will start to
+		// experience stretch ACK problems. This might happen, for instance,
+		// if peer is using PMTUD and the path is constrained. Currently, we
+		// ignore the problem.
+		//
+		// We ack all pushed segments immediately. This improves performance
+		// for interactive traffic, since partial segments typically have the
+		// PSH bit set.
+		//
+		if (iLastAck + iRMSS < iRCV.NXT || immediateAck)
+			SendSegment(KTcpCtlACK);
+		else
+			SendDelayACK();
+		}
+
+	// Wake up transmitter.
+	SchedTransmit();
+
+	//
+	// Wake up application
+	//
+	// Note: We do this even if send window is zero, because
+	// new data from application will trigger zero window probing.
+	//
+	// Note 2: Do not wake up app, if lingering close is pending.
+	//
+	if (iSockFlags.iFlowStopped)
+		{
+		LOG(Log::Printf(_L("\ttcp SAP[%u] ProcessSegments(): FLOW STARTED"), (TInt)this));
+		iSockFlags.iFlowStopped = EFalse;
+		if (iSocket && iSockFlags.iNotify && (iLinger == -1 || !iSockFlags.iSendClose))
+			iSocket->CanSend();
+		}
+	}
+
+
+void CProviderTCP6::SpuriousTimeout(TUint aAcked)
+/**
+Spurious timeout occurred.
+
+Sets the congestion control parameters depending on the ini setting "tcp_spurious_rto_recovery".
+
+@param aAcked	Number of bytes acknowledged with the ACK that triggered this method.
+*/
+	{
+	// TODO: if ACK has ECN-Echo flag, congestion control should not be reverted
+	LOG(Log::Printf(_L("\ttcp SAP[%u] SpuriousTimeout(%u) ENTER: cwnd: %u  ssthresh: %u"), 
+			(TInt)this, aAcked, iCwnd, iSsthresh));
+
+	switch(Protocol()->SpuriousRtoResponse())
+		{
+	case 1:
+	default:
+		// Eifel response (draft uses initial window as burst limit, we use MaxBurst)
+		// Assume that ssthresh was set to FlightSize / 2 when RTO occurred.
+		iSsthresh = iSsthresh << 1;
+		iCwnd = iSND.NXT - iSND.UNA + Min(aAcked, Protocol()->MaxBurst());
+		break;
+		
+	case 2:
+		// Half sending rate (ssthresh has been adjusted when RTO occurred)
+		iCwnd = iSsthresh;
+		break;
+		
+	case 3:
+		// DCLOR - like behaviour (from earlier draft versions)
+		iSsthresh = iSsthresh << 1;
+		iCwnd = 1;
+		}
+	LOG(Log::Printf(_L("\ttcp SAP[%u] SpuriousTimeout() EXIT: cwnd: %u  ssthresh: %u"), 
+			(TInt)this, iCwnd, iSsthresh));
+	// TODO: Do we want to do the RTO adjustment required in Eifel Response draft? Maybe not. -PS
+	}
+
+
+//
+// Initiate a connection by sending the first SYN.
+//
+// Note: The flow MUST be ready when calling this method.
+//
+void CProviderTCP6::SendSYN()
+	{
+	ASSERT(iState == ETcpConnect);
+	ASSERT(iFlow.FlowContext() != 0);
+
+	// Initialise send sequence number
+	iTransmitSeq = iSND.NXT = Protocol()->RandomSequence();
+
+	if (Protocol()->WinScale() != -1)
+		{
+		iRcvWscale = (Protocol()->WinScale() > 0 ? Protocol()->WinScale() - 1 : NeedWindowScale());
+		iOptions.SetWindowScale((TUint8)(iRcvWscale + 1));
+		}
+
+	iRMSS = Min(LinkRMSS(), iSockInBufSize / 2);
+	iOptions.SetMSS(iRMSS);
+	iFreeWindow = Min(iSockInBufSize, 0xffff) % iRMSS;
+	iRCV.WND = Min(iSockInBufSize, 0xffff) - iFreeWindow;
+	iSND.UNA = iSND.NXT;
+
+	TUint8 flags = KTcpCtlSYN;
+	// Start ECN negotiation by setting both ECN bits on in a SYN packet [RFC 3168].
+	if (iFlags.iEcn)
+		{
+		flags |= KTcpCtlECE | KTcpCtlCWR;
+		}
+
+	SendSegment(flags);
+	iTransmitSeq++;
+	EnterState(ETcpSynSent);
+
+	// Disable on-demand interface setup for this flow
+	iFlow.FlowContext()->iInfo.iNoInterfaceUp = 1;
+
+	// Lock source address for this flow. It needs to persist over link change events.
+	iFlow.FlowContext()->iInfo.iLocalSet = 1;
+	}
+
+
+TInt CProviderTCP6::CreateChild(CProviderTCP6*& aSAP)
+	{
+	ASSERT(iConnectCount < iListenQueueSize);
+	ASSERT(iListenQueue);
+
+	// Create a new SAP and initialize it
+	TRAPD(err, aSAP = (CProviderTCP6*)Protocol()->NewSAPL(KSockStream));
+	if (err == KErrNone) //lint -save -esym(613, aSAP) Possible NULL trapped here
+		{
+		aSAP->iParent = this;
+		aSAP->iSockFamily = iSockFamily;
+		aSAP->iAppFamily = iAppFamily;
+
+		// The new SAP is not yet known to socket server
+		aSAP->iSockFlags.iAttached  = EFalse;
+		aSAP->iSockFlags.iNotify    = EFalse;
+		aSAP->iHasNetworkServices = HasNetworkServices();
+		// Copy TCP options to the new socket.
+		aSAP->iSockInBufSize        = iSockInBufSize;
+		aSAP->iSockOutBufSize       = iSockOutBufSize;
+		aSAP->iMSS                  = iMSS;
+		aSAP->iSockFlags.iReuse     = iSockFlags.iReuse;
+		aSAP->iFlags.iOobInline     = iFlags.iOobInline;
+		aSAP->iFlags.iNoDelay       = iFlags.iNoDelay;
+		aSAP->iFlags.iEcn		  = iFlags.iEcn;
+
+		// Clone the flow. Lower layer socket options get copied to the new flow.
+		aSAP->iFlow.Clone(iFlow);
+
+		// Clone currently resets the notifier, so we reinstall it here
+		aSAP->iFlow.SetNotify(aSAP);
+
+		// If the listen queue is full, kill a random connection.
+		if (iConnectCount == iListenQueueSize)
+			{
+			TInt i = Protocol()->Random(iListenQueueSize);
+			CProviderTCP6 *sap = iListenQueue[i];
+			sap->iParent = 0;
+			iListenQueue[i] = iListenQueue[--iConnectCount];
+			iListenQueue[iConnectCount] = 0;
+			sap->Expire();
+			}
+
+		// Add the new connection into listen queue.
+		iListenQueue[iConnectCount++] = aSAP;
+		} //lint -restore
+	return err;
+	}
+
+
+void CProviderTCP6::DetachChild(CProviderTCP6* aSAP)
+	{
+	ASSERT(iConnectCount > 0);
+	ASSERT(iListenQueue);
+
+	// Remove child from listen queue
+	for (TUint i = 0; i < iConnectCount; i++)
+		if (iListenQueue[i] == aSAP)
+		{
+		aSAP->iParent = 0;
+		iListenQueue[i] = iListenQueue[--iConnectCount];
+		iListenQueue[iConnectCount] = 0;
+		return;
+		}
+
+#ifdef _DEBUG
+	User::Panic(_L("CProviderTCP6::DetachChild()"), 0);
+#endif
+	}
+
+TInt CProviderTCP6::CompleteChildConnect(CProviderTCP6* aSAP)
+	{
+	ASSERT(aSAP);
+	ASSERT(iSocket);
+	iChildDeleted = EFalse;
+	iSocket->ConnectComplete(*aSAP);
+	// In some cases SocketServer might have destroyed SAP.
+	// If this happens, return error for deleted SAP.
+	return iChildDeleted ? KErrAbort : KErrNone;
+	}
+
+inline void CProviderTCP6::SetChildDeleted(TBool aDeleted)
+	{
+	iChildDeleted = aDeleted;
+	}
+
+
+//
+// Clear all set option bits for SYNs
+//
+void CProviderTCP6::ClearSYNSettings()
+	{
+	// Remove SYN options from active TCP options.
+	iOptions.SetWindowScale(0);
+	iOptions.ClearMSS();
+	iOptions.ClearSackOk();
+	if (!iFlags.iUseTimeStamps)
+		iOptions.ClearTimeStamps();	
+	}
+
+TUint8 CProviderTCP6::NeedWindowScale()
+	{
+	TUint bufbits = 0;
+#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+	//Window scaling factor needs to be negotiated if the iMaxRecvWin
+	//is more than the 64K, irrespective of the socket receive buffer
+	//size. This will help in achieving the maximum throughput in case
+	//modulation changes
+	if(iTcpMaxRecvWin > iSockInBufSize )
+		{
+		bufbits = iTcpMaxRecvWin >> 16;
+		}
+	else
+#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
+		{
+		bufbits = iSockInBufSize >> 16;
+		}
+	TUint8 scale = 0;
+
+	while (bufbits)
+		{
+		scale++;
+		bufbits >>= 1;
+		}
+
+	return scale;
+	}
+
+TInt CProviderTCP6::CheckPolicy(const TSecurityPolicy& aPolicy, const char *aDiagnostic)
+	{
+	return iParent ? iParent->CheckPolicy(aPolicy, aDiagnostic) : CProviderInet6Transport::CheckPolicy(aPolicy, aDiagnostic);
+	}
+//
+// TCP reassembly queue implementation
+//
+TUint RMBufTcpFrag::Offset()
+	{
+	//LOG(Log::Printf(_L("RMBufTcpFrag::FragOffset()\r\n")));
+
+	TTcpPacket seg(*this);
+	return seg.iHdr->Sequence().Uint32();
+	}
+
+TUint RMBufTcpFrag::FragmentLength()
+	{
+	//LOG(Log::Printf(_L("RMBufTcpFrag::FragLength(): length = %d\r\n"), Length()));
+
+	TTcpPacket seg(*this);
+	return Length() - seg.iHdr->HeaderLength();
+	}
+
+void RMBufTcpFrag::Join(RMBufChain& aSeg)
+	{
+	//LOG(Log::Printf(_L("RMBufTcpFrag::Join()\r\n")));
+
+	TTcpPacket thisSeg(*this), newSeg(aSeg);
+
+	// Remove header and overlapping data from aSeg.
+	aSeg.TrimStart(newSeg.iHdr->HeaderLength() +
+		(Length() - thisSeg.iHdr->HeaderLength()) -
+		(newSeg.iHdr->Sequence() - thisSeg.iHdr->Sequence()));
+
+	// Append.
+	Append(aSeg);
+	}
+
+#ifdef __ARMCC__
+#pragma pop
+#endif