// 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.cpp - TCP protocol for IPv6/IPv4
//

#include "tcp.h"
#include <icmp6_hdr.h>
#include <ip4_hdr.h>
#include "tcpip_ini.h"

// Copied from ip6.cpp. Should move to some common definition file?
static const TLitC8<sizeof(TInt)> KInetOptionDisable = {sizeof(TInt), {0}};

//
// TCP Protocol Description
//

void CProtocolTCP6::Describe(TServerProtocolDesc &aDesc)
	{
	aDesc.iName		  = _S("tcp");
	aDesc.iAddrFamily	  = KAfInet;
	aDesc.iSockType	  = KSockStream;
	aDesc.iProtocol	  = KProtocolInetTcp;
	aDesc.iVersion	  = TVersion(KInet6MajorVersionNumber,
		KInet6MinorVersionNumber,
		KInet6BuildVersionNumber);
	aDesc.iByteOrder	  = EBigEndian;
	aDesc.iServiceInfo	  = KSIStreamBased | KSIInOrder | KSIReliable |
		KSIGracefulClose | KSIPeekData | KSIUrgentData |
		KSIRequiresOwnerInfo;
	aDesc.iNamingServices	  = KNSNameResolution | KNSRequiresConnectionStartup;
	aDesc.iSecurity	  = KSocketNoSecurity;
	aDesc.iMessageSize	  = KSocketMessageSizeIsStream;
	aDesc.iServiceTypeInfo  = ESocketSupport | ETransport | EPreferMBufChains | ENeedMBufs | EUseCanSend;
	aDesc.iNumSockets	  = KUnlimitedSockets;
	}



CProtocolTCP6::CProtocolTCP6()
	{
	__DECLARE_NAME(_S("CProtocolTCP6"));
	}


CProtocolTCP6::~CProtocolTCP6()
	{
	LOG(Log::Printf(_L("\ttcp Deleted")));
	}

CServProviderBase* CProtocolTCP6::NewSAPL(TUint aSockType)
	{
	LOG(Log::Printf(_L("NewSAPL\ttcp SockType=%d)"), aSockType));
	if (aSockType!=KSockStream)
		User::Leave(KErrNotSupported);
	CProviderTCP6 *sap = new (ELeave) CProviderTCP6(this);
	CleanupStack::PushL(sap);
	sap->InitL();
	CleanupStack::Pop();
	LOG(Log::Printf(_L("NewSAPL\ttcp SAP[%u] OK"), (TInt)sap));
	return sap;
	}


void CProtocolTCP6::InitL(TDesC& aTag)
	{
	CProtocolInet6Transport::InitL(aTag);

	iRandomIncrement = 0;
	UserHal::TickPeriod((TTimeIntervalMicroSeconds32&)iClockGranularity);
	}


void CProtocolTCP6::StartL()
	{
	CProtocolInet6Transport::StartL();

	iMSS = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_MSS,
			KTcpDefaultMSS, KTcpMinimumMSS, 65535, ETrue);

	iRecvBuf = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RECV_BUF,
			KTcpDefaultRcvWnd, KTcpMinimumWindow, KTcpMaximumWindow, ETrue); 
	
	iSendBuf = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_SEND_BUF,
			KTcpDefaultSndWnd, KTcpMinimumWindow, KTcpMaximumWindow, ETrue);

	// Argh: ini is in millisecs, but CProtocolTCP6 member is in microsecs.
	iMinRTO = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_MIN_RTO,
			KTcpMinRTO/1000, iClockGranularity/1000, KTcpMaxRTO/1000, ETrue) * 1000;

	iMaxRTO = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_MAX_RTO,
			KTcpMaxRTO/1000, iMinRTO/1000, KTcpMaxRTO/1000, ETrue) * 1000;

	iInitialRTO = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_INITIAL_RTO,
			KTcpInitialRTO/1000, iMinRTO/1000, iMaxRTO/1000, ETrue) * 1000;
			
	iSrttSmooth = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_SRTT_SMOOTH,
			KTcpSrttSmooth, 1, KMaxTInt, ETrue);
			
	iMdevSmooth = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RTTVAR_SMOOTH,
			KTcpMdevSmooth, 1, KMaxTInt, ETrue);

	iRTO_K = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RTO_K,
			KTcpRTO_K, 1, KMaxTInt, ETrue);

	iRTO_G = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RTO_G,
			iClockGranularity/1000, iClockGranularity/1000, KMaxTInt, ETrue) * 1000;

	iMaxBurst = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_MAX_BURST,
			KTcpMaxTransmit, 1, KMaxTInt, ETrue);

	iAckDelay = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_ACK_DELAY,
			KTcpAckDelay/1000, iClockGranularity/1000, KMaxTInt/1000, ETrue) * 1000;

	iSynRetries = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_SYN_RETRIES,
			KTcpSynRetries, 0, KMaxTInt, ETrue);

	iRetries1 = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RETRIES1,
			KTcpMaxRetries1, 0, KMaxTInt, ETrue);

	iRetries2 = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RETRIES2,
			KTcpMaxRetries2, iRetries1, KMaxTInt, ETrue);

	iProbeStyle = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_PROBE_STYLE,	2, 0, 2, EFalse);

	iMsl2Delay = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_MSL2,
			KTcpMsl2Delay/1000, 1, KMaxTInt/1000, ETrue) * 1000;

	iReordering = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_REORDERING,
			KTcpReordering, 0, KMaxTInt, ETrue);

	iInitialCwnd = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_INITIAL_CWND,
			KTcpInitialCwnd, 0, KMaxTInt, ETrue);

	iLtxWindow = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_LTX_WINDOW,
			KTcpLtxWindow, 0, KMaxTInt, ETrue);

	iKeepAliveIntv = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_KEEPALIVE_INTV,
			KTcpKeepAliveIntv, 0, KMaxTInt, ETrue);

	iNumKeepAlives = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_NUM_KEEPALIVES,
			KTcpNumKeepAlives, 0, KMaxTInt, ETrue);

	// keepalive retransmission timer cannot be longer than 30 min, because timers are in microseconds.
	iKeepAliveRxmt = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_KEEPALIVE_RXMT,
			KTcpKeepAliveRxmt, 0, 1800, ETrue);

	iFinPersistency = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_FIN_PERSISTENCY,
			KTcpFinPersistency, 0, iRetries2, ETrue);
	
	// ECN settings: 0 = disabled, 1 = enabled with ECT(1), 2 = enabled with ECT(0)
	iEcn = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_ECN, 0, 0, 2, EFalse);

	iSpuriousRtoResponse = GetIniValue(TCPIP_INI_TCP,
			TCPIP_INI_TCP_SPURIOUS_RTO_RESPONSE, 1, 1, 3, EFalse);

	iWinScale = (TInt8)GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_WINSCALE, 0, -1, 7, ETrue);

	// Flags enabled by default
	iTimeStamps = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_TIMESTAMPS, 1, 0, 1);
	iSack = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_SACK, 1, 0, 1);
	iRFC2414 = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RFC2414, 1, 0, 1);
	iDSack = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_DSACK, 1, 0, 1);
	iFRTO = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_FRTO, 1, 0, 1);

	// Flags disabled by default
	iLocalTimeWait = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_LOCAL_TIMEWAIT, 0, 0, 1);
	iStrictNagle = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_STRICT_NAGLE, 0, 0, 1);
	iPushAck = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_PUSH_ACK, 0, 0, 1);
	iAlignOpt = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_ALIGNOPT, 0, 0, 1);

	// Note: 'dstcache' is in [ip] section, because it is not necessarily TCP specific
	iDstCache = GetIniValue(TCPIP_INI_IP, TCPIP_INI_DSTCACHE, 0, 0, 1);
	
#ifdef SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	iTcpMaxRecvWin = GetIniValue(TCPIP_INI_TCP, TCPIP_INI_TCP_RECV_MAX_WND,
				KTcpDefaultRcvMaxWnd, KTcpMinimumWindow, KTcpMaximumWindow, ETrue);
#endif //SYMBIAN_ADAPTIVE_TCP_RECEIVE_WINDOW
	}


void CProtocolTCP6::Identify(TServerProtocolDesc *aInfo) const
	{
	Describe(*aInfo);
	}

TInt CProtocolTCP6::Send(RMBufChain& aPacket,CProtocolBase* /*aSourceProtocol=NULL*/)
	{
#ifdef _LOGx
	RMBufSendPacket seg;
	seg.Assign(aPacket);
	RMBufSendInfo *info = seg.Unpack();
	LOG(LogPacket('>', seg, info));
	seg.Pack();
	aPacket.Assign(seg);
#endif
	return iNetwork->Send(aPacket);
	}

void CProtocolTCP6::Process(RMBufChain& aPacket, CProtocolBase* /*aSourceProtocol*/)
	{
	RMBufRecvPacket seg;
	seg.Assign(aPacket);
	//  RMBufRecvPacket& seg = (RMBufRecvPacket&)aPacket;
	RMBufRecvInfo* info = seg.Unpack();

#ifdef _LOG
	TBuf<50> src, dst;
	LOG(Log::Printf(_L("\ttcp Process(%d bytes)"), seg.Length()));
#endif

	ASSERT(info->iLength == seg.Length());

	TTcpPacket pkt;
	TInet6Packet<TInet6HeaderIP4> ip4;
	TInet6Packet<TInet6HeaderIP>  ip6;
	TInt err = KErrNone, errMask = MSocketNotify::EErrorAllOperations;
	CProviderTCP6 *sap;
	TInetAddr icmpSender;

	switch (info->iIcmp)
		{
	case 0:

		// Check source and destination addresses
		if (!(TInetAddr::Cast(info->iDstAddr).IsUnicast() && TInetAddr::Cast(info->iSrcAddr).IsUnicast()))
			{
			LOG(Log::Printf(_L("\ttcpProcess() Invalid source or destination address. Packet discarded")));
			seg.Free();
			break;
			}

		pkt.Set(seg, info->iOffset, KTcpMinHeaderLength);

		// Verify header length.
		if (!pkt.iHdr || info->iLength < pkt.iHdr->HeaderLength())
			{
			LOG(Log::Printf(_L("\ttcp Process() Invalid header. Packet discarded")));
			seg.Free();
			return;
			}

		// Get port numbers from header
		info->iSrcAddr.SetPort(pkt.iHdr->SrcPort());
		info->iDstAddr.SetPort(pkt.iHdr->DstPort());

#ifdef _LOG
		LogPacket('<', seg, info, info->iOffset);
		pkt.Set(seg, info->iOffset, pkt.iHdr->HeaderLength()); // LogPacket() may have realigned the header.
#endif

		// Verify TCP checksum
		if (!pkt.VerifyChecksum(seg, info, info->iOffset))
			{
			LOG(Log::Printf(_L("\ttcp Process() Bad checksum. Packet discarded")));
			seg.Free();
			break;
			}

#ifdef _LOGx
		TInetAddr(info->iSrcAddr).OutputWithScope(src); TInetAddr(info->iDstAddr).Output(dst);
		LOG(Log::Printf(_L("\ttcp Process(%d bytes): {%S,%d} -> {%S,%d}"),
			seg.Length(), &src, info->iSrcAddr.Port(), &dst, info->iDstAddr.Port()));
#endif

		// More sanity checking.
		if (info->iSrcAddr.Port() == KInetPortNone ||
			info->iDstAddr.Port() == KInetPortNone)
			{
			LOG(Log::Printf(_L("\ttcp Process() Illegal port number. Packet discarded")));
			seg.Free();
			break;
			}

		sap = (CProviderTCP6*)LocateSap(EMatchServerUnspecAddr,
			info->iVersion == 4 ? KAfInet : KAfInet6,
#ifndef SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
			info->iDstAddr, info->iSrcAddr);
#else
			info->iDstAddr, info->iSrcAddr, NULL, info->iInterfaceIndex);
#endif //SYMBIAN_STRICT_EXPLICIT_SOCKET_BINDING
		if (sap == NULL)
			{
			LOG(Log::Printf(_L("\ttcp Process() No socket. Packet discarded")));
			if (!pkt.iHdr->RST())
				{
				//
				// Send RST.
				//
				if (NetworkService()->Interfacer()->LocalScope(TInetAddr::Cast(info->iDstAddr).Ip6Address(),
					info->iInterfaceIndex, EScopeType_IF))
					{
					if (pkt.iHdr->ACK())
						{
						SendControlSegment(NULL, info->iDstAddr, info->iSrcAddr, KTcpCtlRST,
							pkt.iHdr->Acknowledgment(), 0);
						}
					else
						{
						//TTcpSeqNum seq = pkt.iHdr->Sequence() + aPacket.Length() - pkt.iHdr->HeaderLength();
						TTcpSeqNum seq = pkt.iHdr->Sequence() + seg.Length() -
							pkt.iHdr->HeaderLength() - info->iOffset;

						// Adjust for SYN and FIN
						if (pkt.iHdr->SYN() || pkt.iHdr->FIN())
							seq++;

						SendControlSegment(NULL, info->iDstAddr, info->iSrcAddr, KTcpCtlRST|KTcpCtlACK, 0, seq);
						}
					}
				}
			seg.Free();
			break;
			}

		// Bump it to the SAP.
		seg.Pack();
		sap->Process(seg);
		break;

	case KProtocolInetIcmp:
	case KProtocolInet6Icmp:

        /* From RFC1122:
        4.2.3.9  ICMP Messages
 
            TCP MUST act on an ICMP error message passed up from the IP
            layer, directing it to the connection that created the
            error.  The necessary demultiplexing information can be
            found in the IP header contained within the ICMP message.
 
            o    Source Quench
 
                 TCP MUST react to a Source Quench by slowing
                 transmission on the connection.  The RECOMMENDED
                 procedure is for a Source Quench to trigger a "slow
                 start," as if a retransmission timeout had occurred.
 
            o    Destination Unreachable -- codes 0, 1, 5
 
                 Since these Unreachable messages indicate soft error
                 conditions, TCP MUST NOT abort the connection, and it
                 SHOULD make the information available to the
                 application.
 
                 DISCUSSION:
                      TCP could report the soft error condition directly
                      to the application layer with an upcall to the
                      ERROR_REPORT routine, or it could merely note the
                      message and report it to the application only when
                      and if the TCP connection times out.
 
            o    Destination Unreachable -- codes 2-4
 
                 These are hard error conditions, so TCP SHOULD abort
                 the connection.
 
            o    Time Exceeded -- codes 0, 1
 
                 This should be handled the same way as Destination
                 Unreachable codes 0, 1, 5 (see above).
 
            o    Parameter Problem
 
                 This should be handled the same way as Destination
		Unreachable codes 0, 1, 5 (see above).*/

		//
		// Map the first 8 bytes of TCP segment header
		// (8 bytes is all there is in an ICMPv4 packet)
		//
		// WARNING!!!   Any attempt to access fields other than
		//              SrcPort, DstPort, or Sequence WILL CAUSE
		//              BAD THINGS TO HAPPEN!
		//
		//
		pkt.Set(seg, info->iOffset, 8);
		if (!pkt.iHdr)
			{
			LOG(Log::Printf(_L("\ttcp Process() Invalid TCP header in ICMP")));
			seg.Free();
			break;
			}

		// Get port numbers from header
		info->iSrcAddr.SetPort(pkt.iHdr->SrcPort());
		info->iDstAddr.SetPort(pkt.iHdr->DstPort());

#ifdef _LOG
		TInetAddr(info->iSrcAddr).OutputWithScope(src); TInetAddr(info->iDstAddr).OutputWithScope(dst);
		LOG(Log::Printf(_L("\ttcp Process() ICMP reply to TCP segment {%S,%d} -> {%S,%d}"),
			&src, info->iSrcAddr.Port(), &dst, info->iDstAddr.Port()));
		LOG(Log::Printf(_L("\ttcp Process() ICMP type=%d code=%d param=%d received"),
			info->iType, info->iCode, info->iParameter));
#endif

		sap = (CProviderTCP6*)LocateSap(EMatchServerUnspecAddr,
			info->iVersion == 4 ? KAfInet : KAfInet6,
			info->iSrcAddr, info->iDstAddr);
		if (sap == NULL)
			{
			LOG(Log::Printf(_L("\ttcp Process() No socket. Discarding ICMP message")));
			seg.Free();
			break;
			}

		if (info->iIcmp == KProtocolInetIcmp)
			{
			//
			// ICMPv4 message processing
			//
			ip4.Set(seg, 0, TInet6HeaderIP4::MinHeaderLength());
			if (!ip4.iHdr)
				{
				LOG(Log::Printf(_L("\ttcp SAP[%u] Invalid IPv4 header in ICMP"), (TInt)sap));
				seg.Free();
				break;
				}
			icmpSender.SetAddress(ip4.iHdr->SrcAddr());
			switch (info->iType)
				{
			case KInet4ICMP_Unreachable:
/*
   Code           0 = net unreachable
                  1 = host unreachable
                  2 = protocol unreachable
                  3 = port unreachable
                  4 = fragmentation needed and DF set
                  5 = source route failed
                  6 = destination network unknown
                  7 = destination host unknown
                  8 = source host isolated
                  9 = communication with destination network administratively prohibited
                 10 = communication with destination host administratively prohibited
                 11 = network unreachable for type of service
                 12 = host unreachable for type of service
				*/
				switch (info->iCode)
					{
				case 2:
					err = KErrNoProtocolOpt;
					break;

				case 3:
					err = KErrCouldNotConnect;
					break;

				case 0:                          // Transient condition
					errMask = 0;
					// Fall through

				case 5: case 6: case 8: case 9:  // Persistent conditions
					err = KErrNetUnreach;
					break;

				case 1:                          // Transient condition
					errMask = 0;
					// Fall through

				case 7: case 10: case 12:        // Persistent conditions
					err = KErrHostUnreach;
					break;

				default:
					break;
					}
				break;

			case KInet4ICMP_SourceQuench:        // Do a slow start
				sap->SourceQuench();
				break;

			case KInet4ICMP_TimeExceeded:
				err = KErrNetUnreach;
				errMask = 0;
				break;

			case KInet4ICMP_ParameterProblem:
				err = KErrArgument;
				errMask = 0;
				break;

			default:
				break;
				}
			}
		else
			{
			//
			// ICMPv6 message processing
			//
			ip6.Set(seg, 0, TInet6HeaderIP::MinHeaderLength());
			if (!ip6.iHdr)
				{
				LOG(Log::Printf(_L("\ttcp SAP[%u] Invalid IPv6 header in ICMP"), (TInt)sap));
				seg.Free();
				break;
				}
			icmpSender.SetAddress(ip6.iHdr->SrcAddr());
			switch (info->iType)
				{
			case KInet6ICMP_Unreachable:
/*
   Code           0 - no route to destination
                  1 - communication with destination
                        administratively prohibited
                  2 - (not assigned)
                  3 - address unreachable
                  4 - port unreachable
				*/
				switch (info->iCode)
					{
				case 3:
					err = KErrHostUnreach;
					break;

				case 4:
					err = KErrCouldNotConnect;
					break;

				default:
					err = KErrNetUnreach;
					break;
					}
				break;

			case KInet6ICMP_PacketTooBig:
				//
				// Treat this as a soft error. The flow should
				// automatically adjust to changes in path MTU.
				//
				err = KErrTooBig;
				errMask = 0;
				break;

			case KInet6ICMP_TimeExceeded:
/*
   Code           0 - hop limit exceeded in transit
                  1 - fragment reassembly time exceeded
				*/
				err = KErrNetUnreach;
				errMask = 0;
				break;

			case KInet6ICMP_ParameterProblem:
/*
   Code           0 - erroneous header field encountered
                  1 - unrecognized Next Header type encountered
                  2 - unrecognized IPv6 option encountered
				*/
				err = KErrArgument;
				errMask = 0;
				break;

			default:
				break;
				}
			}

		if (err != KErrNone)
			{
			//
			// TCP only treats ICMP errors as hard errors if it's
			// setting up a new connection. We make ICMP spoofing
			// a little bit more difficult by checking the included
			// TCP sequence number. If the sequence number does not
			// match, we change the error to a soft error.
			//
			if (errMask && pkt.iHdr->Sequence() != sap->iSND.UNA)
				{
				LOG(Log::Printf(_L("\ttcp SAP[%u] TCP sequence mismatch. Converting to soft error"), (TInt)sap));
				errMask = 0;
				}

			LOG(Log::Printf(_L("\ttcp SAP[%u] Reporting ICMP error %d, mask %d.\r\n"), (TInt)sap, err, errMask));
			}

		// Store and report
		sap->IcmpError(err, errMask,
			info->iType, info->iCode,
			TInetAddr::Cast(info->iSrcAddr),
			TInetAddr::Cast(info->iDstAddr),
			TInetAddr::Cast(icmpSender));
		seg.Free();
		break;

	default:
		break;
		}
	}


//
// This routine generates and transmits a TCP control segment.
//
TInt CProtocolTCP6::SendControlSegment(RFlowContext *aFlow,
	const TSockAddr& aSrcAddr, const TSockAddr& aDstAddr,
	TUint8 aFlags, TTcpSeqNum aSeq, TTcpSeqNum aAck,
	TUint32 aWnd, TUint32 aUP)
	{
	LOG(Log::Printf(_L("\ttcp SendControlSegment")));

	RMBufSendPacket seg;
	RMBufSendInfo *info = NULL;
	TInt err;

	// Reserve space for IP+TCP headers and info.
	err = seg.Alloc(TInet6HeaderIP::MaxHeaderLength() + KTcpMinHeaderLength, iBufAllocator);
	if (err != KErrNone)
		return err;
	info = seg.NewInfo();
	if (!info)
		{
		seg.Free();
		return KErrNoMBufs;
		}

	// Leave space for the IP header.
	seg.TrimStart(TInet6HeaderIP::MaxHeaderLength());

	// Fill in info struct
	info->iProtocol = KProtocolInetTcp;
	info->iSrcAddr = aSrcAddr;
	info->iDstAddr = aDstAddr;
	info->iLength = KTcpMinHeaderLength;

	// Open flow context
	if (aFlow != NULL && aFlow->IsOpen())
		{
		err = info->iFlow.Open(*aFlow, info);
		}
	else
		{
		if ((err = info->iFlow.Open(NetworkService(), info->iDstAddr, info->iSrcAddr, info->iProtocol))
				== KErrNone)
			{
			info->iFlow.FlowContext()->SetOption(KSolInetIp, KSoKeepInterfaceUp, KInetOptionDisable);
			}
		}

	if (err != KErrNone)
		{
		// If at first you don't succeed... Well, life is sometimes unforgiving.
		info->iFlow.Close();
		seg.Free();
		return err;
		}

	//
	// Fill in TCP header. Note that the header length has already
	// been set and the checksum will be calculated later.
	//
	TTcpPacket pkt(seg);
	pkt.iHdr->SetHeaderLength(KTcpMinHeaderLength);
	pkt.iHdr->SetSrcPort(info->iSrcAddr.Port());
	pkt.iHdr->SetDstPort(info->iDstAddr.Port());
	pkt.iHdr->SetSequence(aSeq);
	pkt.iHdr->SetAcknowledgment(aAck);
	pkt.iHdr->SetControl(aFlags);
	pkt.iHdr->SetWindow(aWnd);
	pkt.iHdr->SetUrgent(aUP);

	ASSERT(info->iLength == seg.Length());

	//
	// Compute checksum and send the segment.
	//
	pkt.ComputeChecksum(seg, info);
	LOG(LogPacket('>', seg, info));
	seg.Pack();
	Send(seg);

	return KErrNone;
	}


TUint32 CProtocolTCP6::RandomSequence()
	{
	iRandomIncrement += Random(1000000);
	return ((User::TickCount() * iClockGranularity) >> 2) + iRandomIncrement;
	}


#ifdef _LOG
void CProtocolTCP6::LogPacket(char aDir, RMBufChain& aPacket, RMBufPktInfo *info, TInt aOffset)
	{
	RMBufPacketBase pkt;
	pkt.Assign(aPacket);
	TBool packed = EFalse;

	if (info == NULL)
		{
		info = pkt.Unpack();
		packed = ETrue;
		}

	TBuf<0x100> output;
	TBuf<50> src, dst;
	TTcpPacket seg(pkt, aOffset);
	TTcpSeqNum seq = seg.iHdr->Sequence();
	TUint32 len = info->iLength - seg.iHdr->HeaderLength() - aOffset;
	TTcpOptions opt;
	TTime now;
	now.UniversalTime();
#ifdef I64LOW
	TUint32 usec = I64LOW(now.Int64());
#else
	TUint32 usec = now.Int64().GetTInt();
#endif

	TInetAddr(info->iSrcAddr).OutputWithScope(src);
	TInetAddr(info->iDstAddr).OutputWithScope(dst);

	output.Format(_L("\t%6u.%03u "),
		usec / 1000000, (usec / 1000) % 1000);

	if (aDir == '>')
		output.AppendFormat(_L("%S.%u > %S.%u"),
		&src, info->iSrcAddr.Port(), &dst, info->iDstAddr.Port());
	else
		output.AppendFormat(_L("%S.%u < %S.%u"),
		&dst, info->iDstAddr.Port(), &src, info->iSrcAddr.Port());

	_LIT(KHdrSyn, "S");
	_LIT(KHdrFin, "F");
	_LIT(KHdrPsh, "P");
	_LIT(KHdrRst, "R");
	_LIT(KHdrEce, "E");
	_LIT(KHdrCwr, "W");
	_LIT(KHdrDot, ".");
	_LIT(KHdrNot, "");

	output.AppendFormat(_L(" %S%S%S%S%S%S%S %u:%u(%u) wnd=%u"),
		seg.iHdr->SYN() ? &KHdrSyn : &KHdrNot,
		seg.iHdr->FIN() ? &KHdrFin : &KHdrNot,
		seg.iHdr->PSH() ? &KHdrPsh : &KHdrNot,
		seg.iHdr->RST() ? &KHdrRst : &KHdrNot,
		seg.iHdr->ECE() ? &KHdrEce : &KHdrNot,
		seg.iHdr->CWR() ? &KHdrCwr : &KHdrNot,
		!(seg.iHdr->Control() & ~KTcpCtlACK) ? &KHdrDot : &KHdrNot,
		seq.Uint32(), (seq+len).Uint32(), len, seg.iHdr->Window());

	if (seg.iHdr->ACK())
		output.AppendFormat(_L(" ack=%u"), (seg.iHdr->Acknowledgment()).Uint32() );

	if (seg.iHdr->URG())
		output.AppendFormat(_L(" urg=%u"), seg.iHdr->Urgent());

//	output.AppendFormat(_L("\r\n"));
	Log::Write(output);

	if (seg.iHdr->Options(opt) && opt.Length() > 0)
		{
		TUint32 tsVal, tsEcr;
		TInt blockCount = opt.SackBlocks().Count();
		output.Format(_L("\t  options ["));
		if (opt.MSS() >= 0)
			output.AppendFormat(_L(" MSS=%u"), opt.MSS());
		if (opt.SackOk())
			output.AppendFormat(_L(" SackOk"));
		if (opt.TimeStamps(tsVal, tsEcr))
			output.AppendFormat(_L(" TS=%u,%u"), tsVal, tsEcr);
		if (opt.WindowScale())
			output.AppendFormat(_L(" WS=%d"), opt.WindowScale()-1);
			
		if (blockCount)
			{
			SequenceBlockQueueIter iter(opt.SackBlocks());
			SequenceBlock *block;

			output.Append(_L(" SACK="));
			iter.SetToFirst();
			while (block = iter++, block != NULL)
				output.AppendFormat(_L("%u-%u%s"),
				(block->iLeft).Uint32(),
				(block->iRight).Uint32(), --blockCount ? "," : "");
			}
		output.AppendFormat(_L(" ]"));
		Log::Write(output);
		}

	if (packed)
		pkt.Pack();

	aPacket.Assign(pkt);
	}
#endif

