diff -r 000000000000 -r af10295192d8 networkprotocols/tcpipv4v6prt/src/tcp.cpp --- /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 +#include +#include "tcpip_ini.h" + +// Copied from ip6.cpp. Should move to some common definition file? +static const TLitC8 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 ip4; + TInet6Packet 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 +