commsfwutils/commsbufs/reference/loopback_bearer/src/binder.cpp
changeset 0 dfb7c4ff071f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commsfwutils/commsbufs/reference/loopback_bearer/src/binder.cpp	Thu Dec 17 09:22:25 2009 +0200
@@ -0,0 +1,1117 @@
+/*
+* Copyright (c) 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: 
+*
+*/
+#include <e32std.h>
+#include <eui_addr.h>
+#include <ip4_hdr.h>
+#include <udp_hdr.h>
+#include <tcp_hdr.h>
+#include <in_chk.h>
+#include <in_iface.h>
+#include <comms-infras/nifif.h>
+#include <in_sock.h>
+#include "var.h"
+#include "binder.h"
+#include "provision.h"
+
+#ifdef SYMBIAN_ZERO_COPY_NETWORKING
+#include <comms-infras/commsbuf.h>
+#include <comms-infras/commsbufpond.h>
+#include <comms-infras/commsbufpondop.h>
+#else
+#include <comms-infras/mbufmanager.h>
+#endif
+
+using namespace ESock;
+
+#ifdef _DEBUG
+_LIT8(KNif,"Legacy");
+_LIT8(KBinder4,"Binder4");
+_LIT8(KBinder6,"Binder6");
+#endif
+
+//
+// CLegacyLoopbackBinder4 //
+//
+
+CLegacyLoopbackBinder4::CLegacyLoopbackBinder4(CLegacyLoopbackSubConnectionFlow& aLegacyLoopbackSubConnectionFlow) : iLegacyLoopbackSubConnectionFlow(aLegacyLoopbackSubConnectionFlow)
+	{
+    __FLOG_OPEN(KNif, KBinder4);
+   	// generate my local ip address (ip4) - vals potentially will be overwritten by any derived classes
+	iLocalAddressBase = KLegacyLoopbackLocalAddressBase; // also used later in control method
+	
+	TUint32 id = ((TUint32)this) % 255;
+	// Avoid the reserved address (least significant byte KLegacyLoopbackReservedHostId) that
+	// is never to be allocated as a local address.
+	if (id == KLegacyLoopbackReservedHostId)
+		{
+		++id;
+		}
+	iLocalAddress = iLocalAddressBase + id;
+    __FLOG_3(_L8("CLegacyLoopbackBinder4 %08x:\tCLegacyLoopbackBinder4(CLegacyLoopbackSubConnectionFlow& %08x): iLocalAddress %08x"), this, &aLegacyLoopbackSubConnectionFlow, iLocalAddress);
+}
+
+CLegacyLoopbackBinder4::~CLegacyLoopbackBinder4()
+/** 
+Destroys 'this'
+*/
+    {
+    if (iTestSubscriber)
+    	{
+    	iTestSubscriber->Cancel();
+    	}
+    delete iTestSubscriber;
+    delete iDrvReceiver;
+    delete iDrvSender;
+    __FLOG_1(_L8("CLegacyLoopbackBinder4 %08x:\t~CLegacyLoopbackBinder4()"), this);
+    ASSERT(iUpperControl == NULL);
+    ASSERT(iUpperReceiver == NULL);
+	ASSERT(!iErrorOneShot.IsActive());
+    RDebug::Printf("closing driver\n");
+    iDrv.Close();
+    __FLOG_CLOSE;
+    }
+
+CLegacyLoopbackBinder4* CLegacyLoopbackBinder4::NewL(CLegacyLoopbackSubConnectionFlow& aLegacyLoopbackSubConnectionFlow)
+	{
+	CLegacyLoopbackBinder4* self = new (ELeave) CLegacyLoopbackBinder4(aLegacyLoopbackSubConnectionFlow);
+	CleanupStack::PushL(self);
+	
+	const TProviderInfo& providerInfo = static_cast<const TProviderInfoExt&>(
+		aLegacyLoopbackSubConnectionFlow.AccessPointConfig().FindExtensionL(
+			STypeId::CreateSTypeId(TProviderInfoExt::EUid, TProviderInfoExt::ETypeId))).iProviderInfo;
+	TInt ap = providerInfo.APId();
+	if(CLegacyLoopbackFlowTestingSubscriber::ShouldRun(ap))
+		{
+		ASSERT(self->iTestSubscriber==NULL);
+		self->iTestSubscriber = CLegacyLoopbackFlowTestingSubscriber::NewL(aLegacyLoopbackSubConnectionFlow, ap);
+		}
+
+	// The send and receive paths each have an AO
+	self->iDrvReceiver = new(ELeave) CDrvReceiver(CActive::EPriorityHigh);
+	self->iDrvSender = new(ELeave) CDrvSender(CActive::EPriorityHigh);
+
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+void CLegacyLoopbackBinder4::LogPacketDetails(TInet6HeaderIP4* aIp4, const TDesC8& aCaption)
+    {
+    static_cast<void>(aIp4);
+    static_cast<void>(aCaption);
+
+#if defined __FLOG_ACTIVE
+    TBuf8<KLogBufferSize> log;
+    TInet6HeaderUDP* udp = NULL;
+    TInet6HeaderTCP* tcp = NULL;
+    TBuf8<10> prot;
+    TBuf8<40> length;
+    TInt srcPort = -1;
+    TInt dstPort = -1;
+    if(aIp4->Protocol() == KProtocolInetUdp)
+        {
+        udp = (TInet6HeaderUDP*) aIp4->EndPtr();
+        prot = _L8("UDP");
+        length.Format(_L8("payload %d"), udp->Length());
+        srcPort = udp->SrcPort();
+        dstPort = udp->DstPort();
+        }
+    else if(aIp4->Protocol() == KProtocolInetTcp)
+        {
+        tcp = (TInet6HeaderTCP*) aIp4->EndPtr();
+        prot = _L8("TCP");
+        length.Format(_L8("payload %d"), aIp4->TotalLength() - aIp4->HeaderLength() - tcp->HeaderLength());
+        srcPort = tcp->SrcPort();
+        dstPort = tcp->DstPort();
+        }
+    else
+        {
+        prot.Format(_L8("%d"), aIp4->Protocol());
+        length.Format(_L8("tot_len %d"), aIp4->TotalLength());
+        }	
+    TDes8IgnoreOverflow overflow;
+    log.AppendFormat(_L8("%S %S %S src %08x:%d, dst %08x:%d"), &overflow, &aCaption, &prot, &length,
+            aIp4->SrcAddr(), srcPort, aIp4->DstAddr(), dstPort);
+    __FLOG(log);
+#endif    
+    }
+    
+void CLegacyLoopbackBinder4::UpdateHeaders(TInet6HeaderIP4* aIp4, TInet6HeaderUDP* aUdp)
+/**
+Update the IPv4 and UDP headers to allow the packet to be looped back.
+*/
+{
+	__FLOG_STMT( LogPacketDetails(aIp4, _L8("CLegacyLoopbackBinder4:\tUpdateHeaders(...): ")) );
+	
+	// swap over the destination and source addresses
+	TUint32 temp;
+	temp = aIp4->SrcAddr();
+	aIp4->SetSrcAddr(aIp4->DstAddr());
+	aIp4->SetDstAddr(temp);
+
+   	// swap over the destination and source ports
+	if (aUdp)
+		{
+		TUint tempPort;
+		tempPort = aUdp->DstPort();
+		aUdp->SetDstPort(aUdp->SrcPort());
+		aUdp->SetSrcPort(tempPort);
+		}
+
+	// NB: don't need to recalculate any checksums since luckily IP sums don't detect transposition
+	__FLOG_4(_L("CLegacyLoopbackBinder4:\tUpdateHeaders(...): became src %08x:%d, dst %08x:%d"), 
+			aIp4->SrcAddr(), aUdp? (TInt)aUdp->SrcPort(): -1, (TInt)aIp4->DstAddr(), aUdp? (TInt)aUdp->DstPort(): -1);
+}
+
+MLowerDataSender::TSendResult CLegacyLoopbackBinder4::Send(RMBufChain& aData)
+/**
+Entry point for receiving IPv4 outgoing data
+
+@param aData MBuf chain containing data to send
+@return an indication of whether the upper layer should block.
+*/
+	{
+    RMBufPacket& pkt = static_cast<RMBufPacket&>(aData);
+
+    pkt.Unpack();
+	TInet6HeaderIP4* ip4 = (TInet6HeaderIP4*) pkt.First()->Ptr();
+    __FLOG_STMT( LogPacketDetails(ip4, _L8("CLegacyLoopbackBinder4:\tSend(...): ")) );
+
+	if (ip4->Protocol() == KProtocolInetUdp || ip4->Protocol() == KProtocolInetTcp)
+		{
+		// get the "udp" header as well - cheating & relying upon UDP & TCP being in same place
+		TInet6HeaderUDP* udp = (TInet6HeaderUDP*) ip4->EndPtr();
+	
+		// use the source or destination port number to decide whether or not to pass it through the loopback driver
+		// very weak this; dynamic ports could overlap. Really should do this for all packets and
+		// fix any impacted test code
+		TUint srcPort = udp->SrcPort();
+		TUint dstPort = udp->DstPort();
+
+		if(dstPort >= KDriverReflectionRangeStartPort && dstPort <= KDriverReflectionRangeEndPort ||
+			srcPort >= KDriverReflectionRangeStartPort && srcPort <= KDriverReflectionRangeEndPort)
+			{
+			if(!iLoopbackLoadAttempted)
+				{
+				iLoopbackLoadAttempted = ETrue;
+				TInt r = User::LoadPhysicalDevice(KLegacyLoopbackPddFileName);
+				RDebug::Printf("Loading PDD - %d\n", r);
+				if(r == KErrNone || r == KErrAlreadyExists)
+					{
+					r = User::LoadLogicalDevice(KLegacyLoopbackLddFileName);
+					RDebug::Printf("Loading LDD - %d\n", r);
+					if(r == KErrNone || r == KErrAlreadyExists)
+						{
+						r = iDrv.Open();
+						RDebug::Printf("Opening driver - %d\n", r);
+						if(r == KErrNone)
+							{
+							// Prime the pathways
+							iDrvReceiver->Start(this, iDrv);
+							iDrvSender->Start(this, iDrv);
+							}
+						}
+					}
+				}
+			if(iDrv.Handle())
+				{
+				// Swap the ports, because the receipt path also unconditionally swaps them (probably
+				// some test needs this 
+				// ugh! should use a queue here when the write is still busy
+				udp->SetDstPort(srcPort);
+				udp->SetSrcPort(dstPort);
+				
+				__FLOG_4(_L("CLegacyLoopbackBinder4:\tSend(...): passing packet to driver as src %08x:%d, dst %08x:%d"),  
+						ip4->SrcAddr(), dstPort, ip4->DstAddr(), srcPort);
+
+				iDrvSender->QueueForSend(pkt);
+				return ESendAccepted;
+				}
+			}
+		else if(dstPort >= KBearerReflectionRangeStartPort && dstPort <= KBearerReflectionRangeEndPort ||
+					srcPort >= KBearerReflectionRangeStartPort && srcPort <= KBearerReflectionRangeEndPort)
+			{
+			// Swap the ports, because the receipt path also unconditionally swaps them (probably
+			// some test needs this 
+			udp->SetDstPort(srcPort);
+			udp->SetSrcPort(dstPort);
+			}
+		}
+
+	// Loop the data straight back into the TCP/IP stack
+	pkt.Pack();
+	ProcessPacket(aData);
+	return ESendAccepted;
+	}
+	
+
+MLowerDataSender* CLegacyLoopbackBinder4::Bind(MUpperDataReceiver& aUpperReceiver, MUpperControl& aUpperControl)
+/**
+Return the MLowerDataSender instance (CLegacyLoopbackBinder4) that we
+previously allocated.
+*/
+	{
+    __FLOG_3(_L8("CLegacyLoopbackBinder4 %08x:\tBind(aUpperReceiver %08x, aUpperControl %08x)"), this, &aUpperReceiver, &aUpperControl);
+	iUpperReceiver = &aUpperReceiver;
+	iUpperControl = &aUpperControl;
+
+	// Signal upper layer that we are ready
+	BinderReady();
+
+	return this;
+	}
+	
+void CLegacyLoopbackBinder4::Unbind(MUpperDataReceiver& aUpperReceiver, MUpperControl& aUpperControl)
+/**
+Forget our association with upper layer.
+*/
+	{
+    static_cast<void>(aUpperReceiver);
+    static_cast<void>(aUpperControl);
+    __FLOG_1(_L8("CLegacyLoopbackBinder4 %08x:\tUnbind()"), this);
+    ASSERT(&aUpperReceiver == iUpperReceiver);
+    ASSERT(&aUpperControl == iUpperControl);
+    iUpperReceiver = NULL;
+    iUpperControl = NULL;
+	}
+	
+void CLegacyLoopbackBinder4::BinderReady()
+/**
+Signal upper layer that we are ready
+*/
+	{
+    __FLOG_1(_L8("CLegacyLoopbackBinder4 %08x:\tBinderReady()"), this);
+
+	iUpperControl->StartSending();
+	}
+
+void CLegacyLoopbackBinder4::ProcessPacket(RMBufChain& aPdu)
+/**
+Process incoming data
+*/
+    {
+	// this received data has already been looped back...
+	// get the ip header from the RMBufChain
+	TInet6HeaderIP4* ip4 = (TInet6HeaderIP4*) aPdu.First()->Next()->Ptr();
+
+	if (ip4->Protocol() != KProtocolInetUdp && ip4->Protocol() != KProtocolInetTcp)
+		{
+		//Non UDP traffic goes here!
+		__FLOG_3(_L("CLegacyLoopbackBinder4 %08x:\tProcessPacket(): IPv4 length %d, protocol %d"), this, ip4->TotalLength(), ip4->Protocol());
+
+		UpdateHeaders(ip4, NULL);
+		// now process it (pass up the stack)
+		iUpperReceiver->Process(aPdu);
+		return;
+		}
+
+	// get the udp header as well - assume only udp traffic here
+    __FLOG_STMT( LogPacketDetails(ip4, _L8("CLegacyLoopbackBinder4:\tProcessPacket(...): ")) );
+
+	TInet6HeaderUDP* udp = (TInet6HeaderUDP*) ip4->EndPtr();
+
+	// depending on the contents, pass it on up thru the stack 
+	// or maybe do something else
+
+	// use the destination port number to decide whether or not the payload is a command
+	TUint dstPort = udp->DstPort();
+	if (KLegacyLoopbackCmdPort == dstPort)
+    	{
+		// let's use the first payload byte as the command byte
+		switch (*(udp->EndPtr()))
+    		{
+		    case KForceDisconnect:
+			    __FLOG(_L("KForceDisconnect command"));
+			    // do some action
+	            Flow()->Progress(KLinkLayerClosed, KErrCommsLineFail);
+
+	            Flow()->FlowDown(KErrCommsLineFail, MNifIfNotify::EDisconnect);
+
+			    // no return code so all we can do is respond with what we got
+			    UpdateHeaders(ip4, udp);
+			    iUpperReceiver->Process(aPdu);
+			    break;
+
+		    case KForceReconnect:
+			    __FLOG(_L("KForceReconnect command"));
+			    // do some action
+	            Flow()->Progress(KLinkLayerClosed, KErrCommsLineFail);
+	            
+	            Flow()->FlowDown(KErrCommsLineFail, MNifIfNotify::EReconnect);
+
+			    // no return code so all we can do is respond with what we got
+			    UpdateHeaders(ip4, udp);
+			    iUpperReceiver->Process(aPdu);
+			    break;
+
+		    case KSendNotification:
+			    __FLOG(_L("KSendNotification command"));
+			    //let's write the result in the next byte of the reply
+			    if (Flow()->AgentProvision()->IsDialIn() == KErrNotSupported)
+				    udp->EndPtr()[1] = (unsigned char) KErrNone;
+			    else
+				    udp->EndPtr()[1] = (unsigned char) KErrGeneral; // this will lose it's sign :-(
+			
+			    UpdateHeaders(ip4, udp);
+                iUpperReceiver->Process(aPdu);
+			    break;
+			    
+			case KForceFinishedSelection:
+				__FLOG(_L("KForceFinishedSelection command"));
+				// force subConn into KFinishedSelection State
+	            Flow()->Progress(KFinishedSelection, KErrNone);
+				UpdateHeaders(ip4, udp);
+				aPdu.Free();
+				break;
+
+    		case KForceBinderError:
+			    __FLOG(_L("KForceBinderError command"));
+				// We cannot signal an error whilst in the middle of a send in the TCP/IP stack,
+				// as the act of signalling the error will eventually result in the CNifIfBase binder
+				// being destructed by the TCP/IP stack whilst we're in the middle of using it.
+				// Consequently, we would panic on exit from this routine.  So make the call
+				// via an asynchronous callback.
+				if (!iErrorOneShot.IsActive())
+					{
+					iErrorOneShot.Schedule(iUpperControl);
+					}
+    			aPdu.Free();
+    			break;
+
+    		case KColourDataByLinkTierAccessPointId:
+				{
+				const TProviderInfoExt* providerInfo = static_cast<const TProviderInfoExt*>(
+					iLegacyLoopbackSubConnectionFlow.AccessPointConfig().FindExtension(
+						STypeId::CreateSTypeId(TProviderInfoExt::EUid, TProviderInfoExt::ETypeId)));
+				ASSERT(providerInfo); // Should always be present
+
+				// We are going to simply add the access point id to the command byte
+				// A test client can then validate that the socket is connected on the expected access point
+			    __FLOG(_L("KColourDataByAccessPointId command"));
+				*(udp->EndPtr()) += static_cast<TUint8>(providerInfo->iProviderInfo.APId());
+
+				// Update the udp headers and forward on
+				UpdateHeaders(ip4, udp);
+				iUpperReceiver->Process(aPdu);
+				}
+				break;
+
+		    default:
+			    __FLOG(_L("Unknown command - ignoring it"));
+			    aPdu.Free();
+			    // unknown command, just ignore this packet???
+		    }
+	    }
+	else
+	    {
+        __FLOG(_L("Standard echo packet"));
+        if (iTestSubscriber && !iTestSubscriber->IsEnabled())
+        	{
+        	__FLOG(_L("Bearer not available. Packet dropped."));
+        	aPdu.Free();
+        	return;
+        	}
+
+        // just echo the packet back to the original sender
+		// update the headers (addresses, checksums etc)
+		UpdateHeaders(ip4, udp);
+		// now process it (pass up the stack)
+		iUpperReceiver->Process(aPdu);
+		}
+    }
+
+TInt CLegacyLoopbackBinder4::GetConfig(TBinderConfig& aConfig)
+/**
+Return IPv4 configuration information.
+
+Called from upper layer.
+
+@param aConfig structure to populate with IPv4 configuration
+*/
+	{
+    TBinderConfig4* config = TBinderConfig::Cast<TBinderConfig4>(aConfig);
+    
+	if(config == NULL)
+		{
+		return KErrNotSupported;
+		}
+
+	// Setup config
+	config->iInfo.iFeatures = KIfCanBroadcast | KIfCanMulticast;
+	config->iInfo.iMtu = KLoopbackBearerMTU;
+	config->iInfo.iRMtu = KLoopbackBearerMTU;
+	config->iInfo.iSpeedMetric = 0;
+
+	config->iFamily = KAfInet6;
+
+    TUint32 address;
+	const TInt KPort = 65;
+
+    config->iFamily = KAfInet;
+
+	__FLOG_2(_L8("CLegacyLoopbackBinder4 %08x:\tGetConfig(): iLocalAddress %08x"), this, iLocalAddress);        
+	
+	config->iAddress.SetAddress(iLocalAddress);
+	config->iAddress.SetPort(KPort);
+
+	// network mask
+	config->iNetMask.SetAddress(KInetAddrNetMaskC);	// 255.255.255.0
+	config->iNetMask.SetPort(KPort);
+
+	// broadcast address
+	address = iLocalAddressBase + KBroadcastAddressSuffix;
+	config->iBrdAddr.SetAddress(address);
+	config->iBrdAddr.SetPort(KPort);
+
+	// default gateway
+	address = iLocalAddressBase + KDefaultGatewayAddressSuffix;
+	config->iDefGate.SetAddress(address);
+	config->iDefGate.SetPort(KPort);
+
+	// primary DNS, just make same as default gateway
+	config->iNameSer1.SetAddress(address);
+	config->iNameSer1.SetPort(KPort);
+
+	// secondary DNS
+	address = iLocalAddressBase + KSecondaryDnsAddressSuffix;
+	config->iNameSer2.SetAddress(address);
+	config->iNameSer2.SetPort(KPort);
+	
+	return KErrNone;
+	}
+
+TInt CLegacyLoopbackBinder4::Control(TUint aLevel, TUint aName, TDes8& aOption)
+/**
+Called from upper layer for special control functionality.
+*/
+	{	
+	(void) aLevel;
+	(void) aName;
+ 	(void) aOption;
+	__FLOG_3(_L("CLegacyLoopbackBinder4 %08x:\tControl(aLevel %x, aName %x)"), this, aLevel, aName);
+	return KErrNotSupported;
+	}
+
+TInt CLegacyLoopbackBinder4::GetName(TDes& aName)
+/**
+Called from upper layer to retrieve the binder name.
+
+@param aName populated with name
+@return KErrNone on success, else a system wide error code.
+*/
+	{
+	// This name matches the NIF-based DummyNif to match any potential
+	// test code expectations on the name.
+	aName.Format(_L("legacy_loopback[0x%08x]"), this);
+
+	__FLOG_2(_L("CLegacyLoopbackBinder4 %08x:\tGetName(): %S"), this, &aName);
+
+	return KErrNone;
+	}
+
+TInt CLegacyLoopbackBinder4::BlockFlow(MLowerControl::TBlockOption /*aOption*/)
+	{
+    __FLOG_1(_L8("CLegacyLoopbackBinder4 %08x:\tBlockFlow()"), this);
+
+	return KErrNotSupported;
+	}
+
+TBool CLegacyLoopbackBinder4::MatchesUpperControl(ESock::MUpperControl* aUpperControl) const
+/**
+Utility function that returns whether this binder is associated with the
+MUpperControl object passed as argument.
+
+@param aUpperControl upper layer to match against
+@return ETrue on a match else EFalse.
+*/
+	{
+	return aUpperControl == iUpperControl;
+	}
+
+
+CLegacyLoopbackBinder4::CDrvSender::CDrvSender(TInt aPriority)
+: CActive(aPriority)
+	{
+	CActiveScheduler::Add(this);
+    __FLOG_OPEN(KNif, KBinder4);
+	}
+
+CLegacyLoopbackBinder4::CDrvSender::~CDrvSender()
+	{
+	Cancel();
+	do
+		{
+		iSendPkt.Free();
+		}
+	while(iSendQueue.Remove(iSendPkt));
+    __FLOG_CLOSE;
+	}
+
+void CLegacyLoopbackBinder4::CDrvSender::Start(CLegacyLoopbackBinder4* aBinder, RLegacyLoopbackDriver aDrv)
+	{
+	iBinder = aBinder;
+	iDrv = aDrv;
+	}
+
+void CLegacyLoopbackBinder4::CDrvSender::RunL()
+	{
+	// Finished with the buffer just sent
+	iSendPkt.Free();
+
+	// Send the next packet waiting
+	SendPacket();
+	}
+
+void CLegacyLoopbackBinder4::CDrvSender::DoCancel()
+	{
+	iDrv.SendDataCancel();
+	}
+
+void CLegacyLoopbackBinder4::CDrvSender::SendPacket()
+	{
+	// Fetch packet to send
+	TBool havePktToSend = iSendQueue.Remove(iSendPkt);
+	
+	if(havePktToSend)
+		{
+		RMBuf* sendBuffer = NULL;
+
+		// If the packet is composed of more than one buffer then we log this and abandon the packet
+		if(iSendPkt.NumBufs() > 1)
+			{
+			__FLOG_VA((_L8("Warning: packet %d bytes %d buffers"), iSendPkt.Length(), iSendPkt.NumBufs() ));
+			iManyBufCount++;
+
+			// Copy out to a single buffer
+			sendBuffer = RMBuf::Alloc(KLoopbackBearerMTU, iBinder->iAllocator);
+			if(sendBuffer)
+				{
+				sendBuffer->SetLength(0);
+				RMBuf* currentBuf = iSendPkt.First();
+				while(currentBuf)
+					{
+					TPtr8 srcData(currentBuf->Ptr(), currentBuf->Length(), currentBuf->Length());
+					sendBuffer->Append(srcData);
+					currentBuf = currentBuf->Next();
+					}
+				}
+				iSendPkt.Free();
+				RMBufChain sendChain(sendBuffer);
+				iSendPkt.Assign(sendChain);
+			}
+		else
+			{
+			iOneBufCount++;
+			sendBuffer = iSendPkt.First();
+			}
+
+		// Send the buffer
+		if(sendBuffer)
+			{
+			TInt length(sendBuffer->Length());
+			TPtr8 txPtr(sendBuffer->Ptr(), length, length);
+			iDrv.SendData(iStatus, txPtr);
+			SetActive();
+			}
+		}
+	}
+
+void CLegacyLoopbackBinder4::CDrvSender::QueueForSend(RMBufPacket& aPkt)
+	{
+	aPkt.FreeInfo();
+	iSendQueue.Append(aPkt);
+	if(!IsActive())
+		{
+		SendPacket();
+		}
+	}
+	
+CLegacyLoopbackBinder4::CDrvReceiver::CDrvReceiver(TInt aPriority)
+: CActive(aPriority),
+  iRxPtr(NULL, 0)
+	{
+	CActiveScheduler::Add(this);
+	iPond = TCommsBufPondTLSOp::Get();
+	}
+
+CLegacyLoopbackBinder4::CDrvReceiver::~CDrvReceiver()
+	{
+	Cancel();
+	}
+
+void CLegacyLoopbackBinder4::CDrvReceiver::Start(CLegacyLoopbackBinder4* aBinder, RLegacyLoopbackDriver aDrv)
+	{
+	iBinder = aBinder;
+	iDrv = aDrv;
+	RequestReceipt();
+	}
+
+void CLegacyLoopbackBinder4::CDrvReceiver::RequestReceipt()
+	{
+	ASSERT(iPkt.IsEmpty());
+	RMBuf* buf = RMBuf::Alloc(RLegacyLoopbackDriver::KLoopbackMTU);
+	if(buf)
+		{
+		RMBufQ q(buf);
+		TRAPD(ret, iPkt.CreateL(q));	// need non-leaving overload!
+		if(ret == KErrNone)
+			{
+			iPkt.Pack();
+			iRxPtr.Set(buf->Ptr(), 0, buf->Size());
+			}
+		else
+			{
+			// In a real version we could still strive to use the buf, even without the header
+			// But someday we'll be free of having the packet info as the magic extra buffer
+			// on the front & instead pass a concrete type in tandem: type safety & an end to
+			// all this pushing & popping
+			buf->Free();
+			buf = NULL;
+			}
+		}
+	if(!buf)
+		{
+		// No pre-built packet; copy through descriptor
+		iRxPtr.Set(iDesBuf.MidTPtr(0));
+		}
+
+	// Request the next received packet from the driver
+	iDrv.ReceiveData(iStatus, iRxPtr);
+	SetActive();
+	}
+
+void CLegacyLoopbackBinder4::CDrvReceiver::RunL()
+	{
+	TInt ret;
+	if(iPkt.IsEmpty())
+		{
+		TRAP(ret, iPkt.CreateL(iDesBuf));	// need non-leaving overload!
+		if(ret == KErrNone)
+			{
+			iPkt.Pack();
+			}
+		}
+	else
+		{
+		ret = KErrNone;
+		}
+	if(ret == KErrNone)
+		{
+		iBinder->ProcessPacket(iPkt);
+		ASSERT(iPkt.IsEmpty());
+		}
+	RequestReceipt();
+	}
+
+void CLegacyLoopbackBinder4::CDrvReceiver::DoCancel()
+	{
+	iDrv.ReceiveDataCancel();
+	}
+
+
+// =================================================================================
+//
+// CLegacyLoopbackBinder6
+
+CLegacyLoopbackBinder6::CLegacyLoopbackBinder6(CLegacyLoopbackSubConnectionFlow& aLegacyLoopbackSubConnectionFlow) : iLegacyLoopbackSubConnectionFlow(aLegacyLoopbackSubConnectionFlow)
+	{
+    __FLOG_OPEN(KNif, KBinder6);
+    __FLOG_2(_L8("CLegacyLoopbackBinder6 %08x:\tCLegacyLoopbackBinder6(CLegacyLoopbackSubConnectionFlow& %08x)"), this, &aLegacyLoopbackSubConnectionFlow);
+	}
+
+CLegacyLoopbackBinder6::~CLegacyLoopbackBinder6()
+/** 
+Destroys 'this'
+*/
+    {
+    __FLOG(_L8("CLegacyLoopbackBinder6:\t~CLegacyLoopbackBinder6()"));
+    __FLOG_CLOSE;
+    }
+
+CLegacyLoopbackBinder6* CLegacyLoopbackBinder6::NewL(CLegacyLoopbackSubConnectionFlow& aLegacyLoopbackSubConnectionFlow)
+	{
+	return new (ELeave) CLegacyLoopbackBinder6(aLegacyLoopbackSubConnectionFlow);
+	}
+
+void CLegacyLoopbackBinder6::UpdateHeaders(TInet6HeaderIP* aIp6, TInet6HeaderUDP* /*aUdp*/)
+    {
+	// swap over the destination and source addresses
+	TIp6Addr temp;
+	temp = aIp6->SrcAddr();
+	aIp6->SetSrcAddr(aIp6->DstAddr());
+	aIp6->SetDstAddr(temp);
+    }
+    
+MLowerDataSender::TSendResult CLegacyLoopbackBinder6::Send(RMBufChain& aData)
+/**
+Send IPv6 data
+
+Note: not clear that this is properly supported or used.
+
+@param aData data to send
+*/
+	{
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tSend()"));
+
+   	// Loop the data straight back into the TCP/IP stack
+	ProcessPacket(aData);
+	return ESendAccepted;
+	}
+	
+MLowerDataSender* CLegacyLoopbackBinder6::Bind(MUpperDataReceiver& aUpperReceiver, MUpperControl& aUpperControl)
+	{
+    __FLOG_2(_L8("CLegacyLoopbackBinder6:\tBind(MUpperDataReceiver %08x, MUpperControl %08x)"), &aUpperReceiver, &aUpperControl);
+
+	iUpperReceiver = &aUpperReceiver;
+	iUpperControl = &aUpperControl;
+	
+	// Signal upper layer that we are ready
+	BinderReady();
+	
+	return this;
+	}
+
+void CLegacyLoopbackBinder6::BinderReady()
+/**
+Signal to upper layer that we are ready
+*/
+    {
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tBinderReady()"));
+
+	iUpperControl->StartSending();
+    }
+
+void CLegacyLoopbackBinder6::ProcessPacket(RMBufChain& aPdu)
+/**
+Process incoming IPv6 packets.
+
+Note: not clear that this is properly supported or used.
+
+@param aPdu incoming data packet
+*/
+    {
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tProcessPacket()"));
+
+	// this received data has already been looped back...
+	// get the ip header from the RMBufChain
+	TInet6HeaderIP* ip6 = (TInet6HeaderIP*) aPdu.First()->Next()->Ptr();
+	TInet6HeaderUDP* udp = NULL;
+
+	if ((TUint)ip6->NextHeader() == KProtocolInetUdp)
+		{
+		// get the udp header as well - assume only udp traffic here
+		udp = (TInet6HeaderUDP*) ip6->EndPtr();
+
+		__FLOG_3(_L("CLegacyLoopbackBinder6:\tProcessPacket(...): UDP length %d, src port %d, dst port %d"),
+			udp->Length(), udp->SrcPort(), udp->DstPort());
+
+		// depending on the contents, pass it on up thru the stack 
+		// or maybe do something else
+
+		// use the destination port number to decide whether or not the payload is a command
+		TUint dstPort = udp->DstPort();
+		if (KLegacyLoopbackCmdPort == dstPort)
+            {
+ 			// let's use the first payload byte as the command byte
+			switch (*(udp->EndPtr()))
+				{
+			case KForceDisconnect:
+				__FLOG(_L("KForceDisconnect command"));
+				// do some action
+	            Flow()->Progress(KLinkLayerClosed, KErrCommsLineFail);
+
+	            Flow()->FlowDown(KErrCommsLineFail, MNifIfNotify::EDisconnect);
+
+				// no return code so all we can do is respond with what we got
+				UpdateHeaders(ip6, udp);
+				iUpperReceiver->Process(aPdu);
+				break;
+
+			case KForceReconnect:
+				__FLOG(_L("KForceReconnect command"));
+				// do some action
+	            Flow()->Progress(KLinkLayerClosed, KErrCommsLineFail);
+
+                //cause.iReserved=MNifIfNotify::EReconnect;
+	            Flow()->FlowDown(KErrCommsLineFail);
+
+				// no return code so all we can do is respond with what we got
+				UpdateHeaders(ip6, udp);
+				iUpperReceiver->Process(aPdu);
+				break;
+
+			case KSendNotification:
+				__FLOG(_L("KSendNotification command"));
+			    //let's write the result in the next byte of the reply
+			    if (Flow()->AgentProvision()->IsDialIn() == KErrNotSupported)
+				    udp->EndPtr()[1] = (unsigned char) KErrNone;
+			    else
+				    udp->EndPtr()[1] = (unsigned char) KErrGeneral; // this will lose it's sign :-(
+			
+				UpdateHeaders(ip6, udp);
+                iUpperReceiver->Process(aPdu);
+			    break;
+
+
+			default:
+				__FLOG(_L("Unknown command - ignoring it"));
+				break;
+				// unknown command, just ignore this packet???
+				}
+			return;
+			}
+
+		}
+	else
+		{
+		__FLOG_2(_L("CLegacyLoopbackBinder6:\tProcessPacket(...): IPv6 length %d, next header %d"),
+			ip6->PayloadLength(), ip6->NextHeader());
+		}
+
+	// just echo the packet back to the original sender
+
+	// update the headers (addresses, checksums etc).  If "udp" is non-NULL, then
+	// the UDP ports will be updated as well.
+	UpdateHeaders(ip6, udp);
+	// now process it (pass up the stack)
+	iUpperReceiver->Process(aPdu);		
+    }
+
+void CLegacyLoopbackBinder6::Unbind(MUpperDataReceiver& aUpperReceiver, MUpperControl& aUpperControl)
+	{
+    static_cast<void>(aUpperReceiver);
+    static_cast<void>(aUpperControl);
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tUnbind()"));
+
+    ASSERT(&aUpperReceiver == iUpperReceiver);
+    ASSERT(&aUpperControl == iUpperControl);
+    iUpperReceiver = NULL;
+    iUpperControl = NULL;
+	}
+	
+TInt CLegacyLoopbackBinder6::GetName(TDes& aName)
+/**
+Called from upper layer to retrieve the binder name.
+
+@param aName populated with name
+@return KErrNone on success, else a system wide error code.
+*/
+	{
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tGetName()"));
+
+	// This name matches the NIF-based DummyNif to match any potential
+	// test code expectations on the name.
+	aName.Format(_L("legacy_loopback6[0x%08x]"), this);
+	
+	return KErrNone;
+	}
+
+TInt CLegacyLoopbackBinder6::BlockFlow(MLowerControl::TBlockOption /*aOption*/)
+	{
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tBlockFlow()"));
+
+	return KErrNotSupported;
+	}
+
+void CLegacyLoopbackBinder6::StaticDnsConfiguration(TBinderConfig6& aConfig)
+	{
+    __FLOG(_L8("CLegacyLoopbackBinder6:\tStaticDnsConfiguration()"));
+
+	const TLegacyLoopbackIp6Provision* ip6Provision = Flow()->Ip6Provision();
+
+	if (!ip6Provision->Ip6DNSAddrFromServer()) 
+		{
+        aConfig.iNameSer1.SetAddress(ip6Provision->Ip6NameServer1());
+        aConfig.iNameSer2.SetAddress(ip6Provision->Ip6NameServer2());
+		}
+	else
+		{
+		// Ensure that static DNS addresses are set as unspecified,
+		// so they are not used in Control(KSoIfConfig).
+        aConfig.iNameSer1.SetAddress(KInet6AddrNone);
+        aConfig.iNameSer2.SetAddress(KInet6AddrNone);
+		}
+	}
+
+TInt CLegacyLoopbackBinder6::GetConfig(TBinderConfig& aConfig)
+/**
+Return IPv6 configuration information.
+
+Called from upper layer.
+
+@param aConfig structure to populate with IPv6 configuration
+*/
+	{
+    TBinderConfig6* config = TBinderConfig::Cast<TBinderConfig6>(aConfig);
+    
+	if(config == NULL)
+		{
+		return KErrNotSupported;
+		}
+		
+	// Setup config
+	config->iInfo.iFeatures = KIfCanBroadcast | KIfCanMulticast;;
+	config->iInfo.iMtu = KLoopbackBearerMTU;
+	config->iInfo.iRMtu = KLoopbackBearerMTU;
+	config->iInfo.iSpeedMetric = 0;
+	
+	// Setup addresses 
+	
+	config->iFamily = KAfInet6;
+	
+	// Local ID
+	
+	TInt addr64 = 0x80;
+	TE64Addr localAddr(addr64);
+	TEui64Addr* id = (TEui64Addr*) &config->iLocalId;
+	
+	id->Init();
+	id->SetAddress(localAddr);
+	
+	// Remote ID
+	addr64 = 0x81;
+	TE64Addr remoteAddr(addr64);
+	id = (TEui64Addr*) &config->iRemoteId;
+	
+	id->Init();
+	id->SetAddress(remoteAddr);
+	
+	// Setup static DNS address if required
+	StaticDnsConfiguration(*config);
+	    
+    return KErrNone;
+    }
+
+TInt CLegacyLoopbackBinder6::Control(TUint aLevel, TUint aName, TDes8& aOption)
+	{
+	(void) aLevel;
+	(void) aName;
+	(void) aOption;
+	__FLOG_2(_L("CLegacyLoopbackBinder6:\tControl(aLevel %x, aName %x, ...)"), aLevel, aName);
+	return KErrNotSupported;
+	}
+
+//
+// Utilities
+//
+
+TBool CLegacyLoopbackBinder6::MatchesUpperControl(ESock::MUpperControl* aUpperControl) const
+/**
+Utility function that returns whether this binder is associated with the
+MUpperControl object passed as argument.
+
+@param aUpperControl upper layer to match against
+@return ETrue on a match else EFalse.
+*/
+	{
+	return aUpperControl == iUpperControl;
+	}
+
+//
+// Async error callback
+//
+// Used to schedule asynchronous signalling of a binder error to upper flow in circumstances
+// where a direct call is not possible (e.g. in the middle of a TCP/IP send as the binder
+// may disappear underneath the TCP/IP stack).
+
+CLegacyLoopbackErrorOneShot::CLegacyLoopbackErrorOneShot()
+  : CAsyncOneShot(EPriorityStandard)
+	{
+	}
+
+void CLegacyLoopbackErrorOneShot::Schedule(MUpperControl* aUpperControl)
+	{
+	iUpperControl = aUpperControl;
+	Call();
+	}
+
+void CLegacyLoopbackErrorOneShot::RunL()
+	{
+	iUpperControl->Error(KErrCommsLineFail);
+	}
+
+
+CLegacyLoopbackFlowTestingSubscriber::CLegacyLoopbackFlowTestingSubscriber(CLegacyLoopbackSubConnectionFlow& aFlow, TUint aApId)
+	: CActive(0),
+	iFlow(aFlow),
+	iApId(aApId)
+	{
+	}
+
+/*static*/ TBool CLegacyLoopbackFlowTestingSubscriber::ShouldRun(TUint aApId)
+	{
+	RProperty property;
+	TInt result = property.Attach(KLegacyLoopbackTestingPubSubUid, aApId);
+	if(result == KErrNone)
+		{
+		TInt propertyValue;
+		result = property.Get(propertyValue);
+		if(result == KErrNone)
+			{
+			return ETrue;
+			}
+		}
+	return EFalse;
+	}
+
+void CLegacyLoopbackFlowTestingSubscriber::ConstructL()
+	{
+	CActiveScheduler::Add(this);
+//	__DECLARE_NAME(_S("CAvailabilityTestingSubscriber"));
+
+	TInt result = iProperty.Attach(KLegacyLoopbackTestingPubSubUid, iApId);
+	ASSERT(result == KErrNone);
+
+	RunL();
+	}
+
+void CLegacyLoopbackFlowTestingSubscriber::RunL()
+	{
+	// .. and repeat..
+	iProperty.Subscribe(iStatus);
+	
+	TInt publishedValue;
+	TInt result = iProperty.Get(publishedValue);
+	ASSERT(result == KErrNone);
+
+	TAvailabilityStatus av(publishedValue);
+	if (av.Score())
+		{
+		iIsEnabled = ETrue;
+		iFlow.iDisableStart = EFalse;
+		}
+	else
+		{
+		iIsEnabled = EFalse;
+		iFlow.iDisableStart = ETrue;
+		}
+
+	SetActive();
+	}
+
+void CLegacyLoopbackFlowTestingSubscriber::DoCancel()
+	{
+	iProperty.Cancel();
+	}
+
+/*virtual*/ CLegacyLoopbackFlowTestingSubscriber::~CLegacyLoopbackFlowTestingSubscriber()
+	{
+	this->Cancel(); // object must be stoppable by descruction due to cleanup restrictions
+	iProperty.Close();
+	}
+
+
+