networkprotocols/tcpipv4v6prt/src/inet.cpp
changeset 0 af10295192d8
child 53 7e41d162e158
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/inet.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,800 @@
+// 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:
+// inet.cpp - inet protocol base
+//
+
+#include <in_pkt.h>
+#include <ext_hdr.h>
+#include <comms-infras/nifif.h>
+#include <comms-infras/nifif_internal.h>
+#include "inet.h"
+#include "networkinfo.h"
+#include "inet6log.h"
+
+//
+//  CProtocolInet6Base
+//
+//
+
+CProtocolInet6Base::CProtocolInet6Base()
+	{
+	// Nothing here -- rely on CBase zero fill!
+	}
+
+void CProtocolInet6Base::InitL(TDesC& /*aTag*/)
+	{
+#ifdef _LOG
+	// Fetch the protocol name for logging purposes
+	TServerProtocolDesc info;
+	Identify(&info);
+	iName = info.iName;
+#endif
+	}
+
+void CProtocolInet6Base::BindL(CProtocolBase* /*aProtocol*/, TUint /*aId*/)
+	{
+	Panic(EInet6Panic_NotSupported);
+	}
+
+void CProtocolInet6Base::Unbind(CProtocolBase* /*aProtocol*/, TUint /*aId*/)
+	{
+	}
+
+void CProtocolInet6Base::StartL()
+	{
+	}
+
+//
+//	*NOTE*
+//		In the INET6 stack the Error upcall is passed up from the drivers
+//		but, it does not require any actions by default (doing something
+//		here is only useful for protocols modules that want to know about
+//		changes in the interfaces and possibly doing there own
+//		reconfiguring after this (for example, 6to4).
+//
+//		In INET6 the interface manager takes care of notifying individual
+//		flows about the interface changes that affect them. -- msa
+//
+//	This here is required, because the base CProtocolBase::Error()
+//	calls Panic. A similar consideration applies to the StartSending(),
+//	but as the CProtocolBase::StartSending() is already a NOP, nothing
+//	needs to be done here.
+//
+void CProtocolInet6Base::Error(TInt /*anError*/, CProtocolBase* /*aSourceProtocol*/)
+	{
+	}
+
+
+CProtocolInet6Base::~CProtocolInet6Base()
+	{
+	// All SAP's should have been detached from this by now!
+	ASSERT(iSapCount == 0);
+	}
+
+
+//
+// SAP counting
+//
+void CProtocolInet6Base::IncSAPs()
+	{
+	iSapCount++;
+	}
+
+void CProtocolInet6Base::DecSAPs()
+	{
+	ASSERT(iSapCount > 0);
+	iSapCount--;
+	}
+
+//
+// SAP database routines
+//
+void CProtocolInet6Base::BindProvider(CProviderInet6Base* aSAP)
+	{
+#ifdef _LOG
+	TBuf<50> local; aSAP->iFlow.FlowContext()->LocalAddr().OutputWithScope(local);
+	TBuf<50> remote; aSAP->iFlow.FlowContext()->RemoteAddr().OutputWithScope(remote);
+	Log::Printf(_L("\t%S SAP[%u] BindProvider({%S,%d} <--> {%S,%d})"),
+				&ProtocolName(), (TInt)aSAP,
+				&local, aSAP->iFlow.FlowContext()->LocalPort(), &remote, aSAP->iFlow.FlowContext()->RemotePort());
+#endif
+
+	TUint key = ProviderHashKey(aSAP->iFlow.FlowContext()->LocalPort());
+	aSAP->iNextSAP = iSAP[key];
+	iSAP[key] = aSAP;
+	}
+
+void CProtocolInet6Base::QueueBindProvider(CProviderInet6Base* aSAP)
+	{
+#ifdef _LOG
+	TBuf<50> local; aSAP->iFlow.FlowContext()->LocalAddr().OutputWithScope(local);
+	TBuf<50> remote; aSAP->iFlow.FlowContext()->RemoteAddr().OutputWithScope(remote);
+	Log::Printf(_L("\t%S SAP[%u] QueueBindProvider(): {%S,%d} <--> {%S,%d}"),
+			&ProtocolName(), (TInt)this,
+			&local, aSAP->iFlow.FlowContext()->LocalPort(), &remote, aSAP->iFlow.FlowContext()->RemotePort());
+#endif
+
+	TUint key = ProviderHashKey(aSAP->iFlow.FlowContext()->LocalPort());
+	CProviderInet6Base **sap = &iSAP[key];
+	while (*sap != NULL)
+		sap = &(*sap)->iNextSAP;
+	*sap = aSAP;
+	}
+
+void CProtocolInet6Base::UnbindProvider(CProviderInet6Base* aSAP)
+	{
+	CProviderInet6Base **sapPtr;
+	TUint key;
+
+	if (aSAP->iFlow.FlowContext() == NULL)
+		return;
+
+	key = ProviderHashKey(aSAP->iFlow.FlowContext()->LocalPort());
+	for (sapPtr = &iSAP[key]; *sapPtr != NULL; sapPtr = &((*sapPtr)->iNextSAP))
+		if (*sapPtr == aSAP)
+			{
+#ifdef _LOG
+			TBuf<50> local; aSAP->iFlow.FlowContext()->LocalAddr().OutputWithScope(local);
+			TBuf<50> remote; aSAP->iFlow.FlowContext()->RemoteAddr().OutputWithScope(remote);
+			Log::Printf(_L("\t%S SAP[%u] UnbindProvider({%S,%d} <--> {%S,%d})"),
+					&ProtocolName(), (TInt)aSAP,
+					&local, aSAP->iFlow.FlowContext()->LocalPort(), &remote, aSAP->iFlow.FlowContext()->RemotePort());
+#endif
+			*sapPtr = (*sapPtr)->iNextSAP;
+			aSAP->iNextSAP = NULL;
+			return;
+			}
+	}
+
+CProviderInet6Base* CProtocolInet6Base::LocateProvider(TUint aPort)
+	{
+	CProviderInet6Base *sap;
+	TUint key = ProviderHashKey(aPort);
+
+	for (sap = iSAP[key]; sap != NULL; sap = sap->iNextSAP)
+		if (sap->iFlow.FlowContext()->LocalPort() == aPort)
+			return sap;
+
+	return NULL;
+	}
+
+void CProviderInet6Base::InitL()
+	{
+	TInt err = iFlow.Open(iProtocol->NetworkService());
+	if (err != KErrNone)
+ 		User::Leave(err);
+
+	iFlow.SetNotify(this);
+
+	iLastError.iStatus  = KErrNone;
+	iLastError.iErrType = 0;
+	iLastError.iErrCode = 0;
+	iErrorMask          = 0;
+	}
+
+void CProviderInet6Base::Start()
+	{
+#if 0
+  // XXX - Setting socket write buffer to 1024 seems to seriously
+  // hamper TCP performance over loopback. Default is 4096 and 2048
+  // also works ok. Have to look at this later. -ML
+  TVersion v = RNif::Version();
+  TUint32 ver = (v.iMajor << 24) | (v.iMinor << 16) | v.iBuild;
+  if (ver >= 0x01000044)
+	Nif::SetSocketState(ENifBuffers1024, this);
+#endif
+	}
+
+
+//
+//  CProviderInet6Base
+//
+//
+
+CProviderInet6Base::CProviderInet6Base(CProtocolInet6Base* aProtocol) : iProtocol(aProtocol)
+	{
+	LOG(Log::Printf(_L("\t%S SAP[%u] New"), &ProtocolName(), (TInt)this));
+	iNextSAP = NULL;
+	iProtocol->Open();
+	iProtocol->IncSAPs();
+	if (iIsUser == 0)
+		{
+		iProtocol->NetworkService()->IncUsers();
+		iIsUser = 1;
+		}
+	}
+
+CProviderInet6Base::~CProviderInet6Base()
+	{
+	((CProtocolInet6Base*)iProtocol)->UnbindProvider(this);
+	CFlowContext *flow = iFlow.FlowContext();
+	if (flow) 
+		{
+		TPckgBuf< TInt > opt(0);  // Currently there is no use for the option parameter
+		flow->SetOption(KSOLProvider, KSoFlowClosing, opt);
+		}
+	iFlow.Close();
+	if (iIsUser == 1)
+		{
+		iProtocol->NetworkService()->DecUsers();
+		iIsUser = 0;
+		}
+	iProtocol->DecSAPs();
+	LOG(Log::Printf(_L("\t%S SAP[%u] Deleted"), &ProtocolName(), (TInt)this));
+	iProtocol->Close();
+	}
+
+
+void CProviderInet6Base::ActiveOpen()
+	{
+	Panic(EInet6Panic_NotSupported);
+	}
+
+
+TInt CProviderInet6Base::PassiveOpen(TUint /*aQueSize*/)
+	{
+	Panic(EInet6Panic_NotSupported);
+	return 0;
+	}
+
+
+void CProviderInet6Base::ActiveOpen(const TDesC8& /*aConnectionData*/)
+	{
+	Panic(EInet6Panic_NotSupported);
+	}
+
+
+TInt CProviderInet6Base::PassiveOpen(TUint /*aQueSize*/,const TDesC8& /*aConnectionData*/)
+	{
+	Panic(EInet6Panic_NotSupported);
+	return 0;
+	}
+
+void CProviderInet6Base::Shutdown(TCloseType /*option*/,const TDesC8& /*aDisconnectionData*/)
+	{
+	Panic(EInet6Panic_NotSupported);
+	}
+
+// *NOTE*
+//	Why are level KSoInetIfCtrl and KSoInetRtCtrl handled at this
+//	level? Why not just let iProtocol->SetOption() handle them (as
+//	would be logical)? The reason is KSoInetEnum*/KSoInetNext*, which
+//	need a SAP specific context, and that is *NOT* provided by the
+//	iProtocol->SetOption interface. However, only these options are
+//	handled here, rest of the KSoInetRtCtrl/KSoInetIfCtrl are passed
+//	to the protocol -- msa
+//
+TInt CProviderInet6Base::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption)
+	{
+	if (aLevel == KSolInetIp)
+		{
+		// Prefetch the option parameter (error return is ignored,
+		// if the option is not implemented at this level).
+		TInt val;
+		const TInt ret = GetOptionInt(aOption, val);
+
+		switch (aName)
+			{
+		case KSoHeaderIncluded:
+		case KSoRawMode:
+			// *note* Need to arrange KErrNotSupported for TCP! -- msa
+
+			// Implementation of setting HeaderIncluded and RawMode options
+			if (ret != KErrNone)
+				return ret;
+			// [The raw/headerincluded logic here is unnecessarily convoluted.
+			// In EPOC they could be handled independently, as there is no
+			// special priviledges required for setting the raw mode either.
+			// But, because it is specified to work in this way, sigh ...
+			// -- msa]
+			//
+			// Only allow setting of iHeaderIncluded when iRawMode is set
+			// Clearing is always allowed.
+			// When iRawMode==0, then *ALWAYS* iHeaderIncluded == 0
+			if (aName == KSoRawMode)
+				{
+				if (val)
+					iRawMode = 1;
+				else
+					{
+					iRawMode = 0;
+					iHeaderIncluded = 0;
+					}
+				}
+			else if (val == 0)
+				iHeaderIncluded = 0;
+			else if (iRawMode)
+				{
+				iHeaderIncluded = 1;
+				}
+			else
+				return KErrNotSupported; // attempted to set KSoHeaderIncluded without Raw mode
+#ifdef _LOG
+			Log::Printf(_L("SetOpt\t%S SAP[%u] RawMode=%d HeaderInclude=%d"),
+				&ProtocolName(), (TInt)this, iRawMode, iHeaderIncluded);
+#endif
+			return KErrNone;
+
+		case KSoUserSocket:
+			if (ret != KErrNone)
+				return ret;
+#ifdef _LOG
+			Log::Printf(_L("SetOpt\t%S SAP[%u] KSoUserSocket %d -> %d"),
+				&ProtocolName(), (TInt)this, iIsUser, val);
+#endif
+			if ((TUint)val == iIsUser)
+				return KErrNone;	// No change in state
+			else if (val == 0)
+				{
+				iProtocol->NetworkService()->DecUsers();
+				iIsUser = 0;
+				return KErrNone;
+				}
+			else if (val == 1)
+				{
+				iProtocol->NetworkService()->IncUsers();
+				iIsUser = 1;
+				return KErrNone;
+				}
+			else
+				return KErrArgument;
+		default:
+			// For now, give generic LOG here, but should really log at each implementation -- msa
+			LOG(Log::Printf(_L("SetOpt\t%S SAP[%u] level=KSolInetIp name=%d"), &ProtocolName(), (TInt)this, aName));
+			break;
+			}
+		}
+	else if (aLevel == KSolInetIfCtrl)
+		{
+		if (aName == STATIC_CAST(TUint, KSoInetEnumInterfaces))	// See *NOTE* above!
+			{
+			iInterfaceIndex = 0;
+			LOG(Log::Printf(_L("SetOpt\t%S SAP[%u] KSoInetEnumInterfaces"), &ProtocolName(), (TInt)this));
+			return KErrNone;
+			}
+		}
+	else if (aLevel == KSolInetRtCtrl)
+		{
+		if (aName == STATIC_CAST(TUint, KSoInetEnumRoutes))	// See *NOTE* above!
+			{
+			iRouteIndex = 0;
+			LOG(Log::Printf(_L("SetOpt\t%S SAP[%u] KSoInetEnumRoutes"), &ProtocolName(), (TInt)this));
+			return KErrNone;
+			}
+		}
+	else if (aLevel == KNifOptLevel)
+		{
+		return KErrNotSupported;
+		}
+	if (iFlow.FlowContext())
+		{
+		const TInt ret = iFlow.FlowContext()->SetOption(aLevel, aName, aOption);
+		if (ret != KErrNotSupported)
+			return ret;
+		}
+	return iProtocol->Interfacer()->SetOption(aLevel, aName, aOption, *this);
+	}
+
+TInt CProviderInet6Base::GetOption(TUint aLevel, TUint aName, TDes8& aOption) const
+	{
+	LOG(Log::Printf(_L("GetOpt\t%S SAP[%u] level=%x, name=%x, len=%d"), &iProtocol->ProtocolName(), (TInt)this, aLevel, aName, aOption.Length());)
+	if (aLevel == KSolInetIp)
+		switch (aName)
+		{
+		case KSoInetLastError:
+			if (STATIC_CAST(TUint, aOption.MaxLength()) >= sizeof(iLastError))
+				{
+				aOption.SetLength(sizeof(TSoInetLastErr));
+				aOption.Copy((TUint8*)&iLastError, sizeof(iLastError));
+				return KErrNone;
+				}
+			return KErrTooBig;
+			
+		case KSoHeaderIncluded:
+			return SetOptionInt(aOption, iHeaderIncluded);
+		case KSoRawMode:
+			return SetOptionInt(aOption, iRawMode);
+		case KSoUserSocket:
+			return SetOptionInt(aOption, iIsUser != 0);
+
+		default:
+			break;
+		}
+	else if (aLevel == KSolInetIfCtrl && aName == STATIC_CAST(TUint, KSoInetNextInterface))	// See *NOTE* above!
+		{
+		TSoInetInterfaceInfo& opt = *(TSoInetInterfaceInfo*)aOption.Ptr();
+		if (STATIC_CAST(TUint, aOption.MaxLength()) < sizeof(TSoInetInterfaceInfo))
+			return KErrTooBig;
+		(void) new (&opt) TSoInetInterfaceInfo;	// Make sure descriptors are correct.
+		aOption.SetLength(sizeof(TSoInetInterfaceInfo));
+		((CProviderInet6Base *)this)->iInterfaceIndex = iProtocol->Interfacer()->InterfaceInfo(iInterfaceIndex, opt);
+		if (iInterfaceIndex > 0)
+			return KErrNone;
+		else
+			return KErrNotFound;
+		}
+	else if (aLevel == KSolInetRtCtrl && aName == STATIC_CAST(TUint, KSoInetNextRoute))	// See *NOTE* above!
+		{
+		TSoInetRouteInfo& opt = *(TSoInetRouteInfo*)aOption.Ptr();
+		if (STATIC_CAST(TUint, aOption.MaxLength()) < sizeof(TSoInetRouteInfo))
+			return KErrTooBig;
+		aOption.SetLength(sizeof(TSoInetRouteInfo));
+		(void) new (&opt) TSoInetRouteInfo; // Make sure descriptors are correct.
+		((CProviderInet6Base *)this)->iRouteIndex = iProtocol->Interfacer()->RouteInfo(iRouteIndex, opt);
+		if (iRouteIndex > 0)
+			return KErrNone;
+		else
+			return KErrNotFound;
+		}
+	else if (aLevel == KNifOptLevel)
+		{
+		return KErrNotSupported;
+		}
+	//
+	// if none of the above "supported", falls here...
+	//
+	CFlowContext *flow = iFlow.FlowContext();
+	if (flow)
+		{
+		const TInt ret = flow->GetOption(aLevel, aName, aOption);
+		if (ret != KErrNotSupported)
+			return ret;
+		}
+	return iProtocol->Interfacer()->GetOption(aLevel, aName, aOption, *((CProviderInet6Base *)this));
+	}
+
+void CProviderInet6Base::Ioctl(TUint aLevel, TUint aName, TDes8* /*anOption*/)
+	{
+	LOG(Log::Printf(_L("Ioctl\t%S SAP[%u] %x, %x)"), &ProtocolName(), (TInt)this, aLevel, aName));
+	if (aLevel == KSolInetIp && aName == KIoctlInetLastError)
+		{
+		TPckg<TSoInetLastErr> lastError(iLastError);
+		iSocket->IoctlComplete(&lastError);
+		}
+	else
+		Error(KErrNotSupported, MSocketNotify::EErrorIoctl);
+	}
+
+void CProviderInet6Base::CancelIoctl(TUint aLevel, TUint aName)
+	{
+#ifdef _LOG
+	Log::Printf(_L("CancelIoctl\t%S SAP[%u] %x, %x)"), &ProtocolName(), (TInt)this, aLevel, aName);
+#else
+	(void)aLevel;	// silence compiler warning
+	(void)aName;	// silence compiler warning
+#endif
+	// Error call removed. Fixes defect IP/76. -MikaL
+	//Error(KErrNotSupported);
+	}
+
+
+void CProviderInet6Base::Process(RMBufChain& /*aPacket*/, CProtocolBase* /*aSourceProtocol*/)
+	{
+	Panic(EInet6Panic_NotSupported);
+	}
+
+//
+// Error report routine.
+//
+// Soft errors are not immediately reported to the socket server.
+// A soft error is indicated by a zero aOperationMask.
+//
+// The socket error can be cleared by calling this routing with
+// aError == KErrNone.
+//
+void CProviderInet6Base::Error(TInt aError, TUint aOperationMask)
+	{
+	if (aError <= KErrNone && !FatalState())
+		{
+		iLastError.iStatus = aError;
+		if (aError == KErrNone)
+			iErrorMask = aOperationMask;
+		else
+			iErrorMask |= aOperationMask;
+		if (iSocket && aOperationMask)
+			{
+			LOG(Log::Printf(_L("\t%S SAP[%u] Error %d, mask %b"),
+						&ProtocolName(), (TInt)this, aError, aOperationMask));
+			iSocket->Error(aError, aOperationMask);
+	 		}
+		else
+			{
+			LOG(Log::Printf(_L("\t%S SAP[%u] Error %d, mask %b pending"),
+						&ProtocolName(), (TInt)this, aError, aOperationMask));
+			}
+		}
+	else
+		{
+		LOG(Log::Printf(_L("\t%S SAP[%u] Error %d, mask %b ignored"),
+					&ProtocolName(), (TInt) this, aError, aOperationMask));
+		}
+	}
+
+void CProviderInet6Base::SaveIcmpError(TInt aType, TInt aCode, const TInetAddr& aSrcAddr,
+									 const TInetAddr& aDstAddr, const TInetAddr& aErrAddr)
+	{
+	iLastError.iErrType = aType;
+	iLastError.iErrCode = aCode;
+	iLastError.iSrcAddr = aSrcAddr;
+	iLastError.iDstAddr = aDstAddr;
+	iLastError.iErrAddr = aErrAddr;
+	}
+
+void CProviderInet6Base::CanSend()
+	{
+	if(iSocket && /*iFlow.Status() == EFlow_READY && */!FatalState())
+		{
+		LOG(Log::Printf(_L("\t%S SAP[%u] CanSend() Flow UNBLOCKED"), &ProtocolName(), (TInt)this));
+		iSocket->CanSend();
+		}
+	}
+
+void CProviderInet6Base::NoBearer(const TDesC8& aConnectionParams)
+	{
+	if(iSocket)
+		{
+		LOG(Log::Printf(_L("\t%S SAP[%u] NoBearer()"), &ProtocolName(), (TInt)this));
+		iSocket->NoBearer(aConnectionParams);
+		}
+	}
+
+void CProviderInet6Base::Bearer(const TDesC8 &aConnectionInfo)
+	{
+	if(iSocket)
+		{
+		LOG(Log::Printf(_L("\t%S SAP[%u] Bearer()"), &ProtocolName(), (TInt)this));
+		iSocket->Bearer(aConnectionInfo);
+		}
+	}
+
+TInt CProviderInet6Base::CheckPolicy(const TSecurityPolicy& aPolicy, const char *aDiagnostic)
+	{
+	return iSecurityChecker ? iSecurityChecker->CheckPolicy(aPolicy, aDiagnostic) : KErrDisconnected;
+	}
+
+TInt CProviderInet6Base::SecurityCheck(MProvdSecurityChecker *aSecurityChecker)
+	{
+	iSecurityChecker = aSecurityChecker;
+	iHasNetworkServices = CheckPolicy(KPolicyNetworkServices, 0) == KErrNone;
+	return KErrNone;
+	}
+
+TInt CProviderInet6Base::GetOptionInt(const TDesC8 &anOption, TInt &aVal)
+	{
+ 	if (STATIC_CAST(TUint, anOption.Length()) < sizeof(aVal))
+		return KErrTooBig;
+	Mem::Copy(&aVal, anOption.Ptr(), sizeof(aVal));
+	return KErrNone;
+	}
+
+TInt CProviderInet6Base::SetOptionInt(TDes8 &anOption, TInt aVal)
+	{
+	if (STATIC_CAST(TUint, anOption.MaxLength()) < sizeof(aVal))
+		return KErrTooBig;
+	anOption.SetLength(sizeof(aVal));
+	anOption.Copy((TUint8*)&aVal, sizeof(aVal));
+	return KErrNone;
+	}
+
+TInt CProviderInet6Base::Write(RMBufChain& aData, TUint aOptions, TSockAddr* aToAddr)
+	{
+	RMBufSendPacket packet;
+	packet.Assign(aData);	// *beware!* It is legal for aData to be EMPTY!
+#ifdef _LOG
+	TBuf<70> tmp(_L("NULL"));
+#endif
+
+	TInt offset = 0;
+	RMBufSendInfo* info = NULL;
+	TInt err(0);
+	// Allocate the info block for the packet.
+	info = packet.NewInfo();
+	// Because many RMBufChain methods do not handle the empty 
+	// chain "logically", must make sure the chain has at least
+	// one RMBuf in it (without no content). Allocate one and
+	// make its content empty.
+	if (info)
+		{
+		if (packet.IsEmpty())
+			{
+			err = packet.Alloc(0);	// <-- *WARNING* Assumed to allocate single RMBuf
+			if (err == KErrNone)
+				{
+		    	packet.First()->AdjustStart(packet.Length());
+				info->iLength = 0;
+				}
+			}
+		else
+			info->iLength = packet.Length();
+		}
+	else
+		err = KErrNoMBufs; // The info allocation was not successful
+	
+	
+	if(err < 0)
+		{
+		// Treat all leaves in above as no MBufs.
+		LOG(Log::Printf(_L("Write\t%S SAP[%u] No Mbufs (%d)"), &ProtocolName(), (TInt)this, err));
+		err = KErrNoMBufs;
+		goto notify_error;
+		}
+
+#ifdef _LOG
+	if (aToAddr)
+		TInetAddr::Cast(*aToAddr).OutputWithScope(tmp);
+	Log::Printf(_L("Write\t%S SAP[%u] len=%d, opt=%x, to=%S"), &ProtocolName(), (TInt)this, info->iLength, aOptions, &tmp);
+#endif
+
+	//
+	// Initialize the info
+	//
+	TInetAddr::Cast(info->iSrcAddr).Init(0);
+	info->iSrcAddr.SetPort(0);
+	TInetAddr::Cast(info->iDstAddr).Init(0);
+	info->iDstAddr.SetPort(0);
+	//
+	// Get the protocol number to use.
+	// iProtocolId must be NON-ZERO for anything else, except
+	// for IP/IP6 raw sockets. Only those sockets have specified
+	// that the protocol number can be given in port field!
+	info->iProtocol = iProtocolId != 0 ? iProtocolId :
+		aToAddr != NULL ? aToAddr->Port() : 0;
+	info->iFlags = aOptions & (KIpHeaderIncluded | KIpDontFragment);
+	if (iHeaderIncluded)
+		info->iFlags |= KIpHeaderIncluded;
+	(void)new (&info->iFlow) RFlowContext();
+
+	//
+	// Prepare iDstAddr from aToAddr, if specified
+	//
+	if (aToAddr)
+		{
+		info->iDstAddr = *aToAddr;	// Copy as is, including port, flow label and scope.
+		if (info->iDstAddr.Family() != KAfInet6)
+			TInetAddr::Cast(info->iDstAddr).ConvertToV4Mapped();
+		}
+	//
+	// Prepare from included header, if present
+	//
+	err = KErrNotSupported;
+	if (info->iFlags & KIpHeaderIncluded)
+		{
+		// The packet starts with an IPv4 or IPv6 header.
+		TInt next_header = info->iProtocol;
+		TIpHeader *const ip = ((RMBufPacketPeek &)packet).GetIpHeader();
+		if (!ip)
+			{
+			LOG(Log::Printf(_L("\t%S SAP[%u] HeaderIncluded, short/bad IP header"), &ProtocolName(), (TInt)this));
+			goto notify_error;
+			}
+		switch (ip->ip4.Version())
+			{
+			case 4:
+				offset = ip->ip4.HeaderLength();
+				// If protocol is ZERO in header, default to aProtocol. This is
+				// a backward compatibility issue. Applications should not rely
+				// on this. If they are providing the IPv4 header, they SHOULD
+				// ALSO fill the proper protocol number in there.
+				if (!ip->ip4.Protocol())
+					ip->ip4.SetProtocol(next_header);
+				else
+					next_header = ip->ip4.Protocol();
+				TInetAddr::Cast(info->iSrcAddr).SetV4MappedAddress(ip->ip4.SrcAddr());
+				if (aToAddr)
+					ip->ip4.SetDstAddr(TInetAddr::Cast(info->iDstAddr).Address());
+				else
+					TInetAddr::Cast(info->iDstAddr).SetV4MappedAddress(ip->ip4.DstAddr());
+				info->iProtocol = KProtocolInetIp;
+				break;
+			case 6:
+				offset = ip->ip6.HeaderLength();
+				// Note: ZERO NextHeader() does not cause default processing. In
+				// IPv6 ZERO is the Hop-by-Hop extension header and potentially
+				// provided by the application.
+				next_header = ip->ip6.NextHeader();
+				TInetAddr::Cast(info->iSrcAddr).SetAddress(ip->ip6.SrcAddr());
+				if (aToAddr)
+					ip->ip6.SetDstAddr(TInetAddr::Cast(info->iDstAddr).Ip6Address());
+				else
+					TInetAddr::Cast(info->iDstAddr).SetAddress(ip->ip6.DstAddr());
+				info->iProtocol = KProtocolInet6Ip;
+				break;
+			default:
+				LOG(Log::Printf(_L("\t%S SAP[%u] HeaderIncluded, bad IP version (= %d)"), &ProtocolName(), (TInt)this, (TInt)ip->ip4.Version()));
+				goto notify_error;
+			}
+		//
+		// Need to locate the upper layer header
+		//
+		while (info->iProtocol != next_header)
+			{
+			TInet6Packet<TInet6HeaderExtension> hdr(packet, offset);
+			if (hdr.iHdr == NULL || !TPacketPoker::IsExtensionHeader(next_header))
+				{
+				LOG(Log::Printf(_L("\t%S SAP[%u] HeaderIncluded, unknown extension hdr (= %d)"), &ProtocolName(), (TInt)this, next_header));
+				goto notify_error;				
+				}
+			offset += hdr.iHdr->HeaderLength();
+			next_header = hdr.iHdr->NextHeader();
+			}
+		}
+
+	iFlow.SetProtocol(info->iProtocol);
+	err = DoWrite(packet, *info, aOptions, offset);
+	if (err == KErrNone)
+		{
+		// If packet is Empty, assume DoWrite handled it, and
+		// don't send it to the protocol.
+		if (!packet.IsEmpty())
+			{
+			packet.Pack();
+			iProtocol->Send(packet);
+			}
+		goto packet_done;
+		}
+	// DoWrite indicated problem:
+	//	 err > 0, flow cannot receive packet
+	//	 err < 0, some error condition
+	//
+notify_error:
+	if (err >= 0)
+		err = 0;	// Return 0 to indicate that packet has not been accepted
+	else if (err != KErrNoMBufs)
+		{
+		//
+		// Cannot send the packet, report error and drop packet.
+		//
+		Error(err, MSocketNotify::EErrorSend);
+		goto packet_done;
+		}
+	else
+		{
+		if (iSynchSend == 0)
+			{
+			// Out of buffer space (KErrNoMBufs). If iSynchSend is not set, just
+			// drop the packet silently,
+			LOG(Log::Printf(_L("\t%S SAP[%u] Dropping packet for KErrNoMBufs"), &ProtocolName(), (TInt)this));
+			goto packet_done;
+			}
+		LOG(Log::Printf(_L("\t%S SAP[%u] Blocking for KErrNoMBufs"), &ProtocolName(), (TInt)this));
+		}
+
+	// note: err == 0 or KErrNoMBufs!
+	aData.Assign(packet);	// Try to return original chain (approximately correct only
+							// if DoWrite does not add anything to the chain!)
+	packet.Free();			// Need to release info!
+	return err;
+packet_done:
+	// Packet has been sent or dropped
+	packet.Free();			// Cleanup, not always needed (does nothing then).
+	return 1;				// Return 1 to indicate that packet has been accepted
+	}
+
+TUint CProviderInet6Base::Write(const TDesC8& aDesc, TUint aOptions, TSockAddr* aAddr)
+	{
+	LOG(Log::Printf(_L("Write\t%S SAP[%u] Desclen=%d, opt=%x)"), &ProtocolName(), (TInt)this, aDesc.Length(), aOptions));
+	RMBufChain data;
+	if (aDesc.Length() > 0)
+		{
+		TRAPD(err, data.CreateL(aDesc));
+		if (err != KErrNone)
+			{
+			data.Free();	// just to be sure..
+			return 1;
+			}
+		}
+	const TInt ret = Write(data, aOptions, aAddr) != 0; // Return 0 or 1 only.
+	data.Free();
+	return ret;
+	}