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