networkprotocols/tcpipv4v6prt/src/tcp.cpp
changeset 0 af10295192d8
child 25 d15a50675083
child 53 7e41d162e158
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/tcp.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,799 @@
+// 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
+