networksecurity/ipsec/ipsec6/src/sc_prt6.cpp
changeset 0 af10295192d8
child 20 7e41d162e158
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networksecurity/ipsec/ipsec6/src/sc_prt6.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,2304 @@
+// 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:
+// sc_prt.cpp - IPv6/IPv4 security check protocol
+// An instance of a procotol that hooks into IP protocol instance,
+// performing IPSEC checking and processing to packets.
+//
+
+
+#include <posthook.h>
+#include <icmp6_hdr.h>
+#include <udp_hdr.h>
+#include <in_pkt.h>
+#include <ext_hdr.h>
+#include <in6_opt.h>
+#include <in6_event.h>
+#include <es_prot_internal.h>
+
+#include "ipsec.h"
+#include "sc.h"
+#include "epdb.h"
+#include "spdb.h"
+#include "sadb.h"
+#include <networking/ipsecerr.h>
+#include "ipseclog.h"
+#include "tunlintunl.h"
+
+const TIp6Addr KOuterIsInner = {{{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}}};
+
+class CProtocolIpsec;
+class CIpsecHook;
+
+/**
+* Mobility Protocol id.
+* (this should be in insock.h?)
+*/
+const TUint KProtocolInetMh = 135;
+/**
+* SCTP Protocol id
+* (this should be in insock.h?)
+*/
+const TUint KProtocolInetSctp = 132;
+
+/**
+* Max number of nested IPSEC.
+*/
+const int KIpsecMaxNesting = 10;
+
+/**
+* Max number of fragment bookkeeping records.
+*/
+const TInt KIpsecMaxFragmentInfo = 10;
+
+class RHookSA : public RSecurityAssociation
+	/**
+	* Security association handle.
+	*
+	* Extends the RSecurityAssociation handle to include a back link
+	* to the CIpsecHook which actually contains the RHookSA
+	* (for outbound processing)
+	*/
+	{
+public:
+	static void Callback(RSecurityAssociation &aAssociation);
+
+	RHookSA(CFlowContext &aFlow, CPolicyAction *const aItem);
+	/**
+	* The last state of the association.
+	*
+	* iState is mainly used to detect whether initial
+	* acquire failed, when SA dies. If PENDING at DEAD,
+	* then SA died before ever becoming ready,
+	*/
+	TInt iState;
+	CPolicyAction *const iItem;
+private:
+	CFlowContext &iFlow; // ..to change the flow state when SA state changes (see CallBack)
+	};
+
+class TUpperLayerSnoop
+	/**
+	* A snooping class.
+	* A simple class to access the first 4 bytes of the packet.
+	* This is used to snoop the upper layer information.
+	*
+	* Thus max "port offset" = 2 (because port is 16 bits),
+	* and max "type/code offset = 3.
+	*/
+	{
+public:
+	// Basic
+	inline static TInt MinHeaderLength() {return 4; }
+	inline static TInt MaxHeaderLength() {return 4; }
+	// Access values
+	inline TUint16 Port(TUint8 aOff) const { return (i[aOff] << 8) + i[aOff+1]; }
+	inline TUint8 Byte(TUint8 aOff) const { return i[aOff]; }
+private:
+	TUint8 i[4];
+	};
+
+class TSnoopHeader
+	/**
+	* Describes the positions of the port/type/code information in a protocol headers.
+	*/
+	{
+public:
+	TUint8 iProtocol;	//< 0..255
+	TUint8 iSelector;	//< 0..3 (ports, localport, type+code, type)
+	TUint8 iO1;			//< Offset of the source port [0..2] or type [0..3]),
+	TUint8 iO2;			//< Offset of the destination port [0..2] or code [0..3])
+	};
+
+const TUint KLoadPorts = 0;	//< Load local and remote port
+const TUint KLoadLocal = 1;	//< Load local port only
+const TUint KLoadType = 2;	//< Load Type only (into local port as 'type << 8')
+const TUint KLoadBoth = 3;	//< Load Type and code (into local port as 'type << 8 | code')
+
+/**
+* The list of currently known protocols.
+*
+* This table defines the "known protocols" for IPsec, and this must be updated
+* if some new protocol uses ports or type/code system, and which needs to
+* selected in IPsec selectors.
+*/
+static const TSnoopHeader snooper[] =
+	{
+		{ KProtocolInetUdp,		KLoadPorts,	0, 2 },	// UDP uses src and dst port
+		{ KProtocolInetTcp,		KLoadPorts,	0, 2 },	// TCP uses src and dst port
+		{ KProtocolInetIcmp,	KLoadBoth,	0, 1 },	// ICMPv4 uses type and code
+		{ KProtocolInet6Icmp,	KLoadBoth,	0, 1 },	// ICMPv6 uses type and code
+		{ KProtocolInetMh,		KLoadType,	2, 0 },	// Mobile IPv6 uses type
+		{ KProtocolInetSctp,	KLoadPorts, 0, 2 }	// SCTP uses src and dst port
+	};
+static const TSnoopHeader *const snooper_end = &snooper[sizeof(snooper)/sizeof(snooper[0])];
+
+
+class CIpsecHook : public CIpsecReferenceCountObject, public MFlowHook
+	/**
+	* The MFlowHook for outbound IPsec processing.
+	*
+	* An instance of this class is attached to the flows for which
+	* IPsec processing is required.
+	*
+	* The allocation size of this object is variable and it depends
+	* the required IPsec processing. The variable length information
+	* is at the end of fixed member variables consists of two
+	* arrays of structures as follows:
+	* @code
+	* RHookSA[iCount]		// The handles for IPsec associations
+	* RIpAdddress[iTunnels]	// The src addresses of the tunnels (if iTunnels > 0)
+	* @endcode
+	*/
+	{
+private:
+	CIpsecHook(
+		MAssociationManager &aMgr,
+		CFlowContext &aFlow,
+		const RPolicySelectorInfo &aInfo,
+		const TInt aCount,
+		const TInt aTunnels);
+	~CIpsecHook();
+
+	RHookSA &Assoc(TInt aIndex) const
+		{ return ((RHookSA *)(((char *)this) + sizeof(*this)))[aIndex]; }
+	RIpAddress &Tunnel(TInt aIndex) const
+		{ return ((RIpAddress *)&Assoc(iCount))[aIndex]; }
+
+public:
+	// for CProtocolSecpol
+	static CIpsecHook *NewL(
+		MAssociationManager &aMgr,
+		CFlowContext &aFlow,
+		const RPolicySelectorInfo &aInfo,
+		TInt aCount, CPolicyAction **aItems,
+		TInt aTunnels, const RIpAddress *aSrc);
+
+	// MFlowHook
+	void Open();
+	TInt ReadyL(TPacketHead &aHead);
+	TInt ApplyL(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo);
+	void Close();
+	
+private:
+	// The internal state information
+
+	MAssociationManager &iMgr;		//< The association manager.
+	CFlowContext &iFlow;			//< The associated flow
+	RPolicySelectorInfo iInfo;		//< The selector information extracted at OpenL
+	const TUint8 iCount;        	//< Number of IPSEC actions (usually SA) to apply
+	const TUint8 iTunnels;      	//< ..and how many of them include tunnel action
+
+	// Followed by arrays of
+	// - RHookSA[iCount]
+	// - RIpAdddress[iTunnels]
+	};
+
+
+
+RHookSA::RHookSA(CFlowContext &aFlow, CPolicyAction *const aItem) : iItem(aItem), iFlow(aFlow)
+	/**
+	* Constructor.
+	*
+	* @param aFlow The flow.
+	* @param aItem The action
+	*/
+	{
+	iState = EFlow_PENDING;
+	RSecurityAssociation::Init(Callback);
+	aItem->Open();
+	}
+
+void RHookSA::Callback(RSecurityAssociation &aAssociation)
+	/**
+	*  Association has changed state.
+	*
+	* @param aAssociation The handle.
+	*/
+	{
+	const RHookSA &assoc = (RHookSA &)aAssociation;
+
+	switch (assoc.Status())
+		{
+		case SADB_SASTATE_MATURE:
+		case SADB_SASTATE_DYING:
+			// Either of these states means that SA is usable,
+			// The previous state makes no difference.
+			// In case of READY, should probably check all other
+			// required SA's, and not try to wakeup the flow
+			// until ALL of them are available. -- msa
+			if (assoc.iState != KErrNone && assoc.iItem && assoc.iItem->iOptional)
+				assoc.iFlow.SetChanged();	// When optional SA becomes available, "recompute" flow!
+			assoc.iFlow.SetStatus(EFlow_READY);
+			break;
+		case SADB_SASTATE_DEAD:
+			//
+			// The SA is dead. Now it depends on the previous state
+			// whether a new SA negotiation should be started or if
+			// the flow should be shut down.
+			if (assoc.iState == EFlow_PENDING)
+				{
+				//
+				// Flow was waiting for negotiation, which expired
+				//
+				/* here the error value passed by the kmd server
+				 * will be passed to the tcp/ip stack. This error
+				 * value was set in the RSecurityAssociation class.
+				 * 
+				 */
+				TInt errorValue = assoc.ReadErr();
+				if(errorValue==EIpsec_Ok)
+				    {
+                       			 errorValue = EIpsec_AcquireFailed;
+				    }
+				assoc.iFlow.SetStatus(errorValue);
+				break;
+				}
+			// SA was not a LARVAL SA. This means that a real SA
+			// has just expired or been deleted for some reason,
+			// the flow just needs to acquire a new SA.
+			// *FALL THROUGH*
+		default:
+			// SA not available, but may be acquired, set/keep
+			// the flow in pending state (a ReadyL call later
+			// will activate the acquire, if needed).
+			assoc.iFlow.SetStatus(EFlow_PENDING);
+			break;
+		}
+	}
+
+
+class TIpsecFragmentData
+	/**
+	* The state information for single IPsec transfor.
+	*/
+	{
+public:
+	RSecurityAssociation iSA;		//< The IPsec SA that was used
+	TIpAddress iTunnel;				//< The outer Tunnel End Point (src).
+	};
+
+class CIpsecFragmentInfo : public CBase
+	/**
+	* The IPsec fragment tracking.
+	*
+	* When IPsec has been applied to tunneled fragments, it must
+	* be guaranteed that all fragments are correctly protected
+	* as required by the policy. However, the realy policy
+	* requirement is only known after the final fragment has
+	* arrived and when the full packet is available.
+	*
+	* This structure keeps track of applied IPsec for a
+	* packet being assembled.
+	*
+	* This is variable sized object, and the tail end
+	* is an array of state info:
+	* @code TIpsecFragmentData[iCount]
+	* @endcode
+	*/
+	{
+private:
+	CIpsecFragmentInfo(TUint aCount) : iCount(aCount) {}
+public:
+	static CIpsecFragmentInfo *New(TUint aCount);
+	inline TIpsecFragmentData & operator[](TInt aIndex) const;
+	~CIpsecFragmentInfo();
+
+	CIpsecFragmentInfo *iNext;		//< The link to next info block.
+	
+	// Id + src + dst identify the fragment.
+	
+	TUint32 iId;					//< The fragment ID
+	TIpAddress iSrc;				//< The (inner) source address
+	TIpAddress iDst;				//< The (inner) destination address
+	
+	// The applied IPsec
+	const TUint iCount;				//< The number of TIpsecFragmentData that follow after this.
+	
+	// Followed by array of TIpsecFragmentData[iCount]
+	};
+
+class CProtocolSecpol : public CProtocolPosthook, public MSecurityPolicyManager, MEventListener
+	/**
+	* Security Policy component of the IPsec.
+	*
+	* This protocol handles the Security Policy Database and also acts as a
+	* hook for the IP layer.
+	*/
+	{
+public:
+	// ESOCK Protocol Basics
+	CProtocolSecpol();
+	~CProtocolSecpol();
+	void BindToL(CProtocolBase *protocol);
+	void Identify(TServerProtocolDesc *) const;
+	CServProviderBase* NewSAPL(TUint aSockType);
+
+	// for CProtocolIpsec (inbound transforms)
+	TInt TransformL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo);
+
+	// Hook methods (inbound direction)
+	TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo);
+
+	// Hook methods (outbound direction)
+	MFlowHook *OpenL(TPacketHead &aHead, CFlowContext *aFlow);
+
+	// MEventListener
+	void Notify(TUint aEventClass, TUint aEventType, const void *aData);
+
+private:
+	TUint32 GetInterfaceIndex(const TDesC &aName);
+	TBool UpdateInterfaceIndex(const TDesC &aName, TUint32 aIndex);
+	void FixupInterfaceIndexes(CSecurityPolicy *aPolicy);
+
+	// Security Policy Management section
+	inline CSecurityPolicy *Policy() const { return iPolicy; }
+	TInt SetPolicy(const TDesC &aPolicy, TUint &aOffset);
+
+	void Deliver(RMBufPacketBase& aPacket);
+	void NetworkAttachedL();
+	void NetworkDetached();
+	void InterfaceAttached(const TDesC &aName, CNifIfBase *aIf);
+
+	TInt SelectSource(const RIpAddress &aDst, RIpAddress &aSrc) const;
+	TInt CollectBundle(TPolicyFilterInfo &aFilter, RPolicySelectorInfo &aKey, const TInt aMaxItems,
+		CPolicyAction **aItems, RIpAddress *aSrc, TInt &aTunnels) const;
+	void UpdateTunnelInterface(RMBufRecvInfo &aInfo, const CSecurityAssoc *const aSa) /*const*/;
+	void CheckPacketId(RMBufHookPacket &aPacket);
+	TInt CheckFragmentPolicy();
+
+	MAssociationManager *iAssociationManager;
+	CProtocolIpsec *iProtocolIpsec;
+	MEventService *iEventService;
+	CSecurityPolicy* iPolicy;
+	TDblQue<CProviderSecpol> iSAPlist;
+	//
+	// The working space for incoming packet processing
+	//
+	TInt8 iCountSA;			//< Number of slots used from the arrays below
+	TUint iIsFragment:1;	//< Current packet is a fragment.
+	TUint iIsTunnelMode:1;	//< The last transform was in tunnel mode.
+	TUint32 iPacketId;		//< Id of the current packet.
+	TUint32 iId;			//< The fragment id.
+	TIpAddress iSrc;		//< The currently effective src address of incoming packet
+	TIpAddress iDst;		//< The currently effective dst address of incoming packet
+	RPolicySelectorInfo iPktInfo;		//< Incoming packet info
+	RSecurityAssociation iSA[KIpsecMaxNesting];	//< Used Security associations.
+	RIpAddress iTunnel[KIpsecMaxNesting];		//< Related tunnel addresses (outer source)
+	RIpAddress iMyself[KIpsecMaxNesting];		//< Related destination adresses (outer).
+	// List of incomplete fragmented packets.
+	CIpsecFragmentInfo *iFrags;					//< Unfinished fragments.
+
+	};
+
+void IPSEC::IdentifySecpol(TServerProtocolDesc &aEntry)
+	/**
+	* Return IPsec protocol description.
+	*
+	* @retval aEntry The protocol description.
+	*/
+	{
+	_LIT(KSecpol, "secpol6");
+
+	aEntry.iName = KSecpol;;
+	aEntry.iAddrFamily = KAfIpsec;
+	aEntry.iSockType = KSockRaw;
+	aEntry.iProtocol = KProtocolSecpol;
+	aEntry.iVersion = TVersion(1, 0, 0);
+	aEntry.iByteOrder = ELittleEndian;
+	aEntry.iServiceInfo = KSIConnectionLess | KSIMessageBased | KSIBroadcast;
+	aEntry.iNamingServices = 0;
+	aEntry.iSecurity = KSocketNoSecurity;
+	aEntry.iMessageSize = 0xffff;
+	aEntry.iServiceTypeInfo= ESocketSupport | ENeedMBufs;
+	aEntry.iNumSockets = KUnlimitedSockets;
+	}
+
+CProtocolBase *IPSEC::NewSecpolL()
+	/**
+	* Create a new SECPOL protocol instance.
+	*
+	* @return The Secpol
+	*/
+	{
+	return new (ELeave) CProtocolSecpol();
+	}
+	
+//
+
+static void NoCallback(RSecurityAssociation &)
+	/** Dummy do nothing callback */
+	{
+	}
+
+
+// CIpsecFragmentInfo
+//
+
+inline TIpsecFragmentData & CIpsecFragmentInfo::operator[](TInt aIndex) const
+	/*
+	* Return Nth fragment info.
+	* @param aIndex The N value.
+	*/
+	{
+	ASSERT((TUint)aIndex < iCount);
+	return ((TIpsecFragmentData *)((char *)this + sizeof(*this)))[aIndex];
+	}
+		
+CIpsecFragmentInfo *CIpsecFragmentInfo::New(TUint aCount)
+	/**
+	* Create a new fragment info.
+	*
+	* @param aCount The number of TIpsecFragmentData
+	* @return Fragment info block.
+	*/
+	{
+	CIpsecFragmentInfo *f = new (aCount * sizeof(TIpsecFragmentData)) CIpsecFragmentInfo(aCount);
+	if (f)
+		{
+		for (TInt i = 0; i < f->iCount; ++i)
+			(*f)[i].iSA.Init(NoCallback);
+		}
+	return f;
+	}
+
+CIpsecFragmentInfo::~CIpsecFragmentInfo()
+	/**
+	* Desctructor.
+	*/
+	{
+	// Detach the security associations, if any
+	for (TInt i = 0; i < iCount; ++i)
+		{
+		(*this)[i].iSA.None();
+		}
+	}
+
+class CProtocolIpsec : public CIp6Hook
+	/**
+	* Internal protocol for ESP, AH, etc.
+	*
+	* Internal instance which gets to handle the
+	* specific protocols AH, ESP and UPD for inbound
+	* packets.
+	*/
+	{
+public:
+	CProtocolIpsec(CProtocolSecpol &aSecpol) : iSecpol(aSecpol)
+		{};
+	~CProtocolIpsec();
+	void Identify(TServerProtocolDesc *aEntry) const
+		{ IPSEC::IdentifySecpol(*aEntry); }
+	TInt ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+		{ return iSecpol.TransformL(aPacket, aInfo); }
+	/**
+	* The "master" parent.
+	*
+	* This "protocol" has no modifiable members, it borrows
+	* everything from the parent CProtocolSecpol.
+	*/
+	CProtocolSecpol &iSecpol;
+	};
+
+CProtocolIpsec::~CProtocolIpsec()
+	/**
+	* Destructor.
+	*
+	* Cancel all binds with the network using CProtocolInet6Binder::Unbind(),
+	* if the network is still attached
+	*/
+	{
+	if (iSecpol.NetworkService())
+		iSecpol.NetworkService()->Protocol()->Unbind(this, 0);
+	}
+
+//
+
+#ifdef _LOG
+
+static TInt MaskLength(TUint32 aAddr)
+	/**
+	* Return count of contiguous most significant 1-bits in TUint32
+	*/
+	{
+	TInt count = 0;
+	// obviously, this is "brute force" counting
+	while (aAddr & 0x80000000)
+		{
+		count++;
+		aAddr <<= 1;
+		}
+	return count;
+	}
+
+static TInt MaskLength(const TIp6Addr &aAddr)
+	/**
+	* Return count of contiguous most significant 1-bits in IPv6 address.
+	*/
+	{
+	TInt count = 0;
+	for (TUint i = 0; i < sizeof(aAddr.u.iAddr8) / sizeof(aAddr.u.iAddr8[0]); ++i)
+		if (aAddr.u.iAddr8[i] == 0xFF)
+			count += 8;
+		else
+			{
+			count += MaskLength(aAddr.u.iAddr8[i] << 24);
+			break;
+			}
+	return count;
+	}
+
+class TAddressBuf : public TBuf<70>
+	{
+public:
+	TAddressBuf(const TIpAddress &aAddr, TUint16 aPort = 0);
+	};
+
+TAddressBuf::TAddressBuf(const TIpAddress &aAddr, TUint16 aPort)
+	{
+	TInetAddr addr(aAddr, 0);
+	addr.SetScope(aAddr.iScope);
+	addr.OutputWithScope(*this);
+	if (aPort)
+		{
+		_LIT(KFormat, "#%u");
+		AppendFormat(KFormat, (TInt)aPort);
+		}
+	}
+
+static void LogSelectorInfo(const TDesC &aStr, const RPolicySelectorInfo &aSelector, const TPolicyFilterInfo &aFilter)
+	{
+	_LIT(KIn, "inbound ");
+	_LIT(KOut, "outbound ");
+	_LIT(KBoth, "");
+
+	TAddressBuf local(aSelector.iLocal(), (aSelector.iFlags & KTransportSelector_PORTS) == 0 ? 0 : aSelector.iPortLocal);
+	TAddressBuf remote(aSelector.iRemote(), aSelector.iPortRemote);
+
+	const TUint mode = aFilter.iFlags & KPolicyFilter_SYMMETRIC;
+
+	_LIT(KPorts, "%S%Sif=%d local=%S remote=%S prot=%d");
+	_LIT(KTypeCode, "%S%Sif=%d local=%S remote=%S prot=%d type=%d code=%d");
+
+	Log::Printf((aSelector.iFlags & KTransportSelector_PORTS) == 0 ? KTypeCode() : KPorts(),
+		&aStr,
+		mode == KPolicyFilter_INBOUND ? &KIn() : mode == KPolicyFilter_OUTBOUND ? &KOut() : &KBoth(),
+		(TInt)aFilter.iIndex,
+		&local, &remote,
+		(TInt)aSelector.iProtocol,
+		(TInt)((aSelector.iPortLocal >> 8) & 0xFF),
+		(TInt)(aSelector.iPortLocal & 0xFF));
+	}
+
+static void LogBundleItem(const TDesC &aStr, const CPolicyAction &aItem, const CSecurityAssoc *aSA = NULL)
+	{
+	_LIT(KTunnel, "tunnel");
+	_LIT(KOptional, "?");
+	_LIT(KObligatory, "");
+
+	TBuf<15> spi;
+	if (aSA)
+		{
+		spi.Format(_L(" spi %u"), ByteOrder::Swap32(aSA->SPI()));
+		}
+	TAddressBuf tmp(aItem.iTunnel());
+	Log::Printf(_L("%S%S%S(%S)%S"),
+		&aStr,
+		aItem.iOptional ? &KOptional : &KObligatory, 
+		aItem.iSpec == NULL ? &KTunnel : aItem.iSpec->iName,
+		&tmp,
+		&spi);
+	}
+
+static void LogAddressSelector(TDes &aBuf, const TDesC &aLabel, const TIpAddress &aAddr, const TIpAddress &aMask)
+	{
+	if (!aMask.IsUnspecified() || aMask.iScope)
+		{
+		TBuf<70> str;
+		TInetAddr addr(aAddr, 0);
+		addr.SetScope(aAddr.iScope);
+		addr.Output(str);
+		aBuf.AppendFormat(_L("%S%S "), &aLabel, &str);
+		const TInt mlen = MaskLength(aMask);
+		if (mlen < 128)
+			{
+			// Non-trivial mask, output it.
+			TInetAddr msk(aMask, 0);
+			if (aAddr.IsV4Mapped() && mlen >= 64)
+				msk.SetAddress(BigEndian::Get32(&aMask.u.iAddr8[12]));
+			msk.Output(str);
+			aBuf.AppendFormat(_L(" mask=%S "), &str);
+			}
+		}
+	}
+
+static void LogPolicySelector(const TDesC &aStr, const CPolicySelector &aPS)
+	{
+	// The following code assumes that partial masks (not covering the full field) are
+	// only possible for address fields. For others, the non-zero mask is assumed to be
+	// all-ones.
+	TBuf<200> buf(aStr);
+	if (aPS.iFilterData & KPolicyFilter_FINAL)
+		buf.Append(_L("final "));
+	if ((aPS.iFilterMask & KPolicyFilter_MERGE) == 0)
+		buf.Append(_L("merge "));
+	switch (aPS.iFilterData & KPolicyFilter_SYMMETRIC)
+		{
+	case KPolicyFilter_INBOUND:
+		buf.Append(_L("inbound "));
+		break;
+	case KPolicyFilter_OUTBOUND:
+		buf.Append(_L("outbound "));
+		break;
+	default:
+		break;
+		}
+	if (aPS.iInterface)
+		buf.AppendFormat(_L("if=%d "), aPS.iInterface->iInterfaceIndex);
+	for (CTransportSelector *ts = aPS.iTS; ts != NULL; ts = ts->iOr)
+		{
+		LogAddressSelector(buf, _L("remote="), ts->iData.iRemote(), ts->iMask.iRemote());
+		LogAddressSelector(buf, _L("local="),  ts->iData.iLocal(),  ts->iMask.iLocal());
+		if (ts->iData.iFlags & KTransportSelector_PORTS)
+			{
+			if (ts->iMask.iPortLocal)
+				buf.AppendFormat(_L("local_port=%u "), ts->iData.iPortLocal);
+			if (ts->iMask.iPortRemote)
+				buf.AppendFormat(_L("remote_port=%u "), ts->iData.iPortRemote);
+			}
+		else
+			{
+			if (ts->iMask.iPortLocal & 0xFF00)
+				buf.AppendFormat(_L("type=%u "), ts->iData.iPortLocal >> 8);
+			if (ts->iMask.iPortLocal & 0x00FF)
+				buf.AppendFormat(_L("code=%u"), ts->iData.iPortLocal & 0xFF);
+			}
+		Log::Write(buf);
+		buf = _L("\t\t| ");
+		}
+	Log::Write(buf);
+	}
+
+#endif
+
+//
+
+
+CProtocolSecpol::CProtocolSecpol()
+	/**
+	* Constructor.
+	*/
+	{
+	iSAPlist.SetOffset(_FOFF(CProviderSecpol,iSAPlink));
+
+	// The inbound associations don't need any special processing
+	// for SA status changes. Just put dummy callback routine in.
+	for (TInt i = KIpsecMaxNesting; --i >= 0; )
+		iSA[i].Init(NoCallback);
+	}
+
+CProtocolSecpol::~CProtocolSecpol()
+	/**
+	* Destructor.
+	*/
+	{
+	// Deregister event notifications, if still active
+	if (iEventService)
+		iEventService->RemoveListener(this);
+	
+	// Detach all linked assocations, if any left
+	for (TInt i = KIpsecMaxNesting; --i >= 0; )
+		iSA[i].None();
+
+	delete iPolicy;
+	iPolicy = NULL;
+
+	delete iProtocolIpsec;
+
+	// Cleanup the fragment information
+	while (iFrags)
+		{
+		CIpsecFragmentInfo *tmp = iFrags;
+		iFrags = iFrags->iNext;
+		delete tmp;
+		}
+
+	if (iAssociationManager)
+		{
+		iAssociationManager->Close();
+		iAssociationManager = NULL;
+		}
+
+	if (NetworkService())
+		{
+		// The IPsec is going away, all flows need to be
+		// re-connected...
+		NetworkService()->SetChanged();
+		}
+
+	// If assumptions are correct, iSAPlist should be empty!!
+	ASSERT(iSAPlist.IsEmpty());
+#if __WINS__
+	LOG(Log::Printf(_L("CProtocolSecpol::~CProtocolSecpol() done IPSEC_OBJECT_COUNT=%d"), IPSEC_OBJECT_COUNT));
+#endif
+	}
+
+void CProtocolSecpol::Identify(TServerProtocolDesc *aInfo) const
+	/**
+	* @retval aInfo A minimal TServerProtocolDesc to satisfy the Socket server.
+	*/
+	{
+	IPSEC::IdentifySecpol(*aInfo);
+	}
+
+TUint32 CProtocolSecpol::GetInterfaceIndex(const TDesC &aName)
+	/**
+	* Find interface index by interface name.
+	*
+	* Query the interface index from the network layer. The query uses
+	* @code
+	* MInterfaceManager::GetOption(KSolInetIfQuery, KSoInetIfQueryByName, opt),
+	* @endcode
+	* where the opt is TSoInetIfQuery.
+	*
+	* @param aName The interface name
+	* @return The interface index (= 0, if not found)
+	*/
+	{
+	MInterfaceManager *const mgr = NetworkService()->Interfacer();
+	TPckgBuf<TSoInetIfQuery> opt;
+	opt().iName = aName;
+	opt().iIndex = 0;
+	// Ignore GetOption error. If interface does not exist yet,
+	// this returns ZERO.
+	(void)mgr->GetOption(KSolInetIfQuery, KSoInetIfQueryByName, opt);
+	return opt().iIndex;
+	}
+
+TBool CProtocolSecpol::UpdateInterfaceIndex(const TDesC &aName, TUint32 aIndex)
+	/**
+	* Update interface index in the cached interface in the SPD.
+	*
+	* @param aName The interface name
+	* @param aIndex The interface index.
+	* @return ETrue, if name exists, and EFalse otherwise.
+	*
+	* The CSelectorInterface contains a cached interface in CSecurityPolicy.
+	*
+	* If the aIndex == 0, then ask the interface index (GetInterfaceIndex())
+	* from the network service.
+	*
+	* If the aIndex > 0, assume it is correct for the named interface
+	* (not verified).
+	*/
+	{
+	if (iPolicy == NULL)
+		return EFalse;
+	for (CSelectorInterface *si = iPolicy->iInterfaces; si != NULL; si = si->iNext)
+		{
+		if (aName.Compare(*si->iName) == 0)
+			{
+			if (aIndex != 0 || (aIndex = GetInterfaceIndex(aName)) != 0)
+				{
+				LOG(Log::Printf(_L("<>\tipsec Interface Index Update IF [%S] index %d -> %d"),
+						&aName, si->iInterfaceIndex, aIndex));
+				si->iInterfaceIndex = aIndex;
+				}
+			// The name was located!
+			return ETrue;
+			}
+		}
+	// The name not located.
+	return EFalse;
+	}
+
+
+// CProtocolSecpol::InterfaceAttached
+// **********************************
+void CProtocolSecpol::InterfaceAttached(const TDesC &aName, CNifIfBase *)
+	/**
+	* Interface has attached to the network layer.
+	*
+	* This exists only because the interface create/destroy does not
+	* currently generate events. And, this event only occurs when a
+	* real NIF attached.
+	*
+	* The IPsec will not know if someone creates a "virtual" interface
+	* and does not specify any addresses, or otherwise modify it.
+	*
+	* At this point the interface index is not known, use the UpdateInterfaceIndex()
+	* without specified index (aIndex = 0), and it will query the index, if this
+	* interface is referenced in the current policy.
+	*
+	* @param aName The interface name
+	* @param aIf Not used.
+	*/
+	{
+	(void)UpdateInterfaceIndex(aName, 0);
+	}
+
+
+// CProtocolSecpol::Notify
+// ***********************
+void CProtocolSecpol::Notify(TUint aEventClass, TUint aEventType, const void *aData)
+	{
+	(void)aEventType; // (just silence warning for release compilation)
+	if (aEventClass != EClassInterface)
+		return;
+	
+	const TInetInterfaceInfo &ii = *(TInetInterfaceInfo *)aData;
+
+	if (UpdateInterfaceIndex(ii.iName, ii.iIndex))
+		return;
+	LOG(Log::Printf(_L("<>\tipsec Interface Event(%d) IF [%S] index %d not used in policy"),
+					aEventType, &ii.iName, ii.iIndex));
+	}
+
+
+//	*********************************
+//	CProtocolSecpol::NetworkAttachedL
+//	*********************************
+//	Do the bindings to the IP layer
+void CProtocolSecpol::NetworkAttachedL()
+	/**
+	* The bind to TCP/IP stack has been processed.
+	*
+	* The TCP/IP stack (MNetworkService) is now available and IPSEC can
+	* add the required hooks to it and do other initializations.
+	*/
+	{
+	// If policy is loaded, need to redo the interfaces
+	// (network may have been down, and indexes changed)
+	if (iPolicy)
+		FixupInterfaceIndexes(iPolicy);
+
+	// Create inbound internal protocol only when really needed.
+	if (iProtocolIpsec == NULL)
+		iProtocolIpsec = new (ELeave) CProtocolIpsec(*this);
+
+	NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetAh));
+	NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetEsp));
+	NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetUdp));
+
+	NetworkService()->BindL(this, BindHookAll());    // All inbound packets
+	NetworkService()->BindL(this, BindFlowHook(1));  // All outbound packets
+
+	// To handle tunneled fragments within IPSEC, need to catch the IPv6
+	// Fragment Header and IPv4-in-IP headers
+	NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInet6Fragment));
+	NetworkService()->BindL(iProtocolIpsec, BindHookFor(KProtocolInetIpip));
+
+	iEventService = IMPORT_API_L(NetworkService()->Interfacer(), MEventService);
+	iEventService->RegisterListener(this, EClassInterface);
+	}
+
+//	********************************
+//	CProtocolSecpol::NetworkDetached
+//	********************************
+void CProtocolSecpol::NetworkDetached()
+	{
+	/**
+	* The biding to TCP/IP is being cut.
+	*
+	* Do only the internal housekeeping. The calling CProtocolPostHook code
+	* does all the necessary protocol Unbinding from the network, this does
+	* not need Unbind explicitly any of the bindings done in NetworkAttachedL.
+	*/
+	ASSERT(iEventService != NULL);
+
+	iEventService->RemoveListener(this);
+	iEventService = NULL;
+	delete iProtocolIpsec;
+	iProtocolIpsec = NULL;
+	}
+
+//  ************************
+//  CProtocolSecpol::BindToL
+//  ************************
+void CProtocolSecpol::BindToL(CProtocolBase *aProtocol)
+	/**
+	* Bind Secpol protocol to other protocols.
+	*
+	* This calls comes from the socket server, when it processed the "bindto/bindfrom" directives
+	* of the ESK files: "[secpol] bindto= ip6,pfkey". The secpol only accepts protocols
+	* KProtocolInet6Ip and KProtocolKey. Trying to bind anything else will result an error leave.
+	*
+	* The base class CProtocolPostHook::DoBindToL handles the KProtoclInet6Ip (network) binding
+	* and calls NetworkAttachedL method, it that happens.
+	* This code only needs to recognize the PFKEY (KProtocolKey) binding. The PFKEY provides
+	* MAssociationManager.
+	*
+	* @param aProtocol The target protocol to bind to.
+	*/
+	{
+	const TUint id = (TUint)DoBindToL(aProtocol);
+	if (iAssociationManager == NULL && (iAssociationManager = IPSEC::FindAssociationManager(aProtocol, id)) != NULL)
+		{
+		iAssociationManager->Open();
+		}
+	else if (id)
+		User::Leave(KErrGeneral);
+	}
+
+// **********
+// CIpsecHook
+// **********
+CIpsecHook::CIpsecHook(
+	MAssociationManager &aMgr,
+	CFlowContext &aFlow,
+	const RPolicySelectorInfo &aInfo,
+	const TInt aCount,
+	const TInt aTunnels) :
+   iMgr(aMgr), iFlow(aFlow), iInfo(aInfo), iCount((TUint8)aCount), iTunnels((TUint8)aTunnels)
+	{
+	iMgr.Open();	// Association manager needs exist while this CIpsecHook exists.
+	};
+
+// ***********************
+// CIpsecHook::~CIpsecHook
+// ***********************
+CIpsecHook::~CIpsecHook()
+	{
+	// Detach associated Security Assocations
+	for (TInt i = 0; i < iCount; ++i)
+		{
+		RHookSA &h = Assoc(i);
+		h.None();
+		h.iItem->Close();
+		}
+
+	// Close tunnel src address endpoints
+	for (TInt j = 0; j < iTunnels; ++j)
+		{
+		RIpAddress &addr = Tunnel(j);
+		addr.Close();
+		}
+
+#ifdef IPSEC_OBJECT_TRACKING
+	// Close the remote/local RIpAddress (not really
+	// necessary, but automatic destructors are run
+	// so late that object counting debug would cause
+	// spurious panic...
+	iInfo.iRemote.Close();
+	iInfo.iLocal.Close();
+#endif
+
+	iMgr.Close();	// Release the association manager
+	LOG(Log::Printf(_L("\tMFlowHook[%u] destruct"), (TUint)this));
+	}
+
+// ****************
+// CIpsecHook::NewL
+// ****************
+CIpsecHook *CIpsecHook::NewL
+	(MAssociationManager &aMgr,
+	CFlowContext &aFlow,
+	const RPolicySelectorInfo &aInfo,
+	TInt aCount, CPolicyAction **aItems,
+	TInt aTunnels, const RIpAddress *aSrc)
+	/**
+	* Create a new IPSEC hook handle object.
+	*
+	* Allocating space only as much as is absolutely necessary.
+	*
+	* @param aMgr		The association manager
+	* @param aFlow		The flow context
+	* @param aInfo		The policy selector information
+	* @param aCount		The number of transforms (includes plain tunnels)
+	* @param aItems		The transforms (aCount)
+	* @param aTunnels	The number of tunnels within transforms
+	* @param aSrc		The tunnel source addresses
+	*/
+	{
+	const TInt extra = aCount * sizeof(RHookSA) + aTunnels * sizeof(RIpAddress);
+
+	CIpsecHook *const handle = new (extra) CIpsecHook(aMgr, aFlow, aInfo, aCount, aTunnels);
+	if (handle == NULL)
+		User::Leave(KErrNoMemory);
+	
+	// Initialize RAssociation Hooks
+	for (TInt i = 0; i < aCount; ++i)
+		(void)new (&handle->Assoc(i)) RHookSA(aFlow, aItems[i]);
+
+	// Initialize tunnel srsc addresses
+	for (TInt j = 0; j < aTunnels; ++j)
+		{
+		(void)new (&handle->Tunnel(j)) RIpAddress(aSrc[j]);
+		}
+	return handle;
+	}
+
+TInt CProtocolSecpol::SelectSource(const RIpAddress &aDst, RIpAddress &aSrc) const
+	/**
+	* Select the source address for a destination.
+	*
+	* @param aDst The destination address
+	* @retval aSrc The selected source address
+	* @return KErrNone
+	*/
+	{
+	TIp6Addr src;
+	if (NetworkService()->Interfacer()->CheckRoute(aDst(), aDst().iScope, src) != KErrNone)
+		return EIpsec_NoInnerSource;    // Could not find the src addres!
+	// ARGHH! Checkroute does not return the source scope id...
+	// NEED to fetch some value by uncertain methods..
+	TUint32 scope = NetworkService()->Interfacer()->LocalScope(src, aDst().iScope, (TScopeType)(aDst().Scope()-1));
+	aSrc.Set(src, scope);
+	return KErrNone;
+	}
+
+
+static void AfterTunnelAction(RPolicySelectorInfo &aInfo)
+	/**
+	* Change policy selector to match state after applied tunnel.
+	*
+	* - ports = 0
+	* - protocol #KProtocolInetIpip or #KProtocolInet6Ipip
+	* 
+	* Internal help function. Assume iLocal/iRemote have already
+	* been updated, if required.
+	*/
+	{
+	aInfo.iPortLocal = 0;
+	aInfo.iPortRemote = 0;
+	aInfo.iFlags &= ~KTransportSelector_PORTS;
+	aInfo.iProtocol = aInfo.iRemote().IsV4Mapped() ? KProtocolInetIpip : KProtocolInet6Ipip;
+	}
+
+TInt CProtocolSecpol::CollectBundle(TPolicyFilterInfo &aFilter, RPolicySelectorInfo &aKey, const TInt aMaxItems,
+	 CPolicyAction **aItems , RIpAddress *aSrc, TInt &aTunnels) const
+	/**
+	* Match the packet against the policy selectors and collect the IPsec actions.
+	*
+	* The function may modify the aFilter and aKey parameters.
+	* 
+	* @retval aFilter The filter
+	* @retval aKey The packet information
+	* @param aMaxItems Max limit for actions.
+	* @retval aItems The collected actions.
+	* @retval aSrc The current source address.
+	* @retval aTunnels The number of tunnels.
+	* @return
+	*	@li >= 0. The number of actions.
+	*	@li EIpsec_NoSelectorMatch, packet does not match policy
+	*	@li EIpsec_MaxTransforms, too many actions triggered.
+	*	@li EIpsec_NoInnerSource, cannot select tunnel source (error is now misnamed).
+	*/
+	{
+	TInt result = EIpsec_NoSelectorMatch;
+	aTunnels = 0;
+	TInt i = 0;
+	for (CPolicySelector *ps = iPolicy->iSelectors; ps != NULL; ps = ps->iNext)
+		{
+		// 1. Check filtering
+		if (((aFilter.iFlags ^ ps->iFilterData) & ps->iFilterMask) ||
+			(ps->iInterface && ps->iInterface->iInterfaceIndex != aFilter.iIndex))
+			continue;	// -- this selector filtered out, try next.
+		// 2. check transport selectors (empty TS matches all)
+		if (ps->iTS == NULL || ps->iTS->Match(aKey))
+			{
+			if (ps->iFilterData & KPolicyFilter_DROP)
+				return EIpsec_NoSelectorMatch;
+
+			const TInt N = ps->iActions.Count();
+			for (TInt k = 0; k < N; k++)
+				{
+				CPolicyAction *const action = ps->iActions[k];
+				if (action == NULL)
+					continue;
+				if (i == aMaxItems)
+					return EIpsec_MaxTransforms;
+				aItems[i++] = action;
+				if (!action->iTunnel().IsNone())
+					{
+					// After tunneling, any futher matching is based on the
+					// selectors of a tunnel (addresses only, no ports)
+					aFilter.iFlags |= KPolicyFilter_TUNNEL;
+					if (!KOuterIsInner.IsEqual(action->iTunnel()))
+						{
+						aKey.iRemote = action->iTunnel;
+						if (SelectSource(aKey.iRemote, aKey.iLocal) != KErrNone)
+							return EIpsec_NoInnerSource;
+						}
+					AfterTunnelAction(aKey);
+					// Return list of source addresses, if the target array is supplied
+					if (aSrc)
+						aSrc[aTunnels] = aKey.iLocal;
+					aTunnels++;
+					}
+				}
+			result = i;
+			if (ps->iFilterData & KPolicyFilter_FINAL)
+				break;	// Final selector, no merge allowed!
+			aFilter.iFlags |= KPolicyFilter_MERGE;	// Mark "merge only".
+			}
+		}
+	return result;
+	}
+
+
+
+MFlowHook *CProtocolSecpol::OpenL(TPacketHead &aHead, CFlowContext *aFlow)
+	/**
+	* Attach IPsec processing to a flow (if needed).
+	*
+	* OpenL is called once when the flow is opened. It should decide
+	* whether further calls are necessary (ReadyL/ApplyL) for this flow.
+	*
+	* When called, the IPsec selector information is in TPacketHead.
+	*	
+	* If OpenL() notices that the flow will never be opened,
+	* it will leave with error, causing the flow to be cancelled.
+	*
+	* The OpenL tasks:
+	*
+	* @li	Consult the Security Policy and find out what actions
+	*		need to be done for this flow: reject, pass clear or
+	*		apply IPsec.
+	* @li	If there are IPsec actions, then allocate a
+	*		CIpHook object to handle the processing of the flow.
+	* @li	If the actions include tunneling, update the flow
+	*		selector information to match the outermost tunnel.
+
+	* @param aHead	The flow information
+	* @param aFlow	The flow context (always NON-NULL)
+	*
+	* @return CIpsecHook or NULL.
+	*/
+	{
+	if (Policy() == NULL)
+		{
+		LOG(Log::Printf(_L("CProtocolSecpol::OpenL() -- No IPSEC policy loaded")));
+		return NULL;    // No Policy, no IPSEC available
+		}
+	if (!iAssociationManager)
+		{
+		LOG(Log::Printf(_L("CProtocolSecpol::OpenL() -- PFKEY not avaiable")));
+		return NULL;    // No CProtocolKey, no IPSEC available
+						// (should this cause packet drops instead?)
+		}
+
+	TPolicyFilterInfo filter;
+	filter.iIndex = aHead.iInterfaceIndex;
+	filter.iFlags = KPolicyFilter_OUTBOUND;
+
+	iPktInfo.FillZ();
+	iPktInfo.iProtocol = aHead.iProtocol;
+	for (const TSnoopHeader *f = snooper; f < snooper_end; ++f)
+		{
+		if (iPktInfo.iProtocol == f->iProtocol)
+			{
+			if (f->iSelector <= KLoadLocal)
+				{
+				iPktInfo.iFlags |= KTransportSelector_PORTS;
+				iPktInfo.iPortLocal = aHead.iSrcPort;
+				iPktInfo.iPortRemote = aHead.iDstPort;
+				}
+			else
+				{
+				iPktInfo.iPortLocal = (TUint16)((aHead.iIcmpType << 8) | aHead.iIcmpCode);
+				iPktInfo.iPortRemote = iPktInfo.iPortLocal;
+				}	
+			break;
+			}
+		}
+	iPktInfo.iLocal.Set(aHead.ip6.SrcAddr(), aHead.iSrcId);
+	iPktInfo.iRemote.Set(aHead.ip6.DstAddr(), aHead.iDstId);
+
+	CPolicyAction *items[KIpsecMaxNesting];		// Collected actions [0..count-1]
+
+	TInt tunnels = 0;
+	LOG(LogSelectorInfo(_L("OpenL\t"), iPktInfo, filter));
+	RPolicySelectorInfo final_info = iPktInfo;
+	TInt count = CollectBundle(filter, final_info, KIpsecMaxNesting, items, iMyself, tunnels);
+	if (count < 0)
+		{
+		LOG(Log::Printf(_L("\t*DROP*")));
+		User::Leave(count);
+		}
+	else if (count == 0)
+		{
+		ASSERT(tunnels == 0);
+		LOG(Log::Printf(_L("\t*PASS*")));
+		return NULL;
+		}
+	ASSERT(tunnels <= count);
+
+	CIpsecHook *handle = CIpsecHook::NewL(*iAssociationManager, *aFlow, iPktInfo, count, items, tunnels, iMyself);
+
+	LOG(Log::Printf(_L("\tMFlowHook[%u] attached"), (TUint)handle));
+
+	if (tunnels)
+		{
+		// If tunneling happened, then update the packet head
+		// information accordingly.
+		
+		aHead.ip6.SetDstAddr(final_info.iRemote());
+		aHead.iDstId = final_info.iRemote().iScope;
+		aHead.iSourceSet = 1;
+		aHead.ip6.SetSrcAddr(final_info.iLocal());
+		aHead.iSrcId = final_info.iLocal().iScope;
+		aHead.iProtocol = final_info.iProtocol;
+		aHead.iDstPort = 0;
+		aHead.iSrcPort = 0;
+		aHead.iIcmpType = 0;
+		aHead.iIcmpCode = 0;
+		// should set proper tunnel protocol? 4 or 41 depending on outermost inner header?
+		aHead.ip6.SetVersion(aHead.ip6.DstAddr().IsV4Mapped() ? 4 : 6);
+
+		// Comment on setting: aHead.iFragment = 1
+		// ---------------------------------------
+		// It is possible to request fragmentation before the
+		// IPSEC processing, if the IPSEC is adding the tunnel.
+		// *HOWEVER* This would be legal ONLY IF the tunneling
+		// SA is the FIRST (or only) transformation. If the policy
+		// actions says something like
+		//		ESP + AH(tunnel)
+		// then ESP is in transport mode, and applying that to
+		// fragments is not ALLOWED. The fragmenting should happen
+		// AFTER the ESP transformation, and before the tunneling.
+		//
+		// This is not possible with current IPSEC hook.
+		// To enable such feature, either
+		// 1) IPSEC must do the fragmenting self
+		// 2) IPSEC processing needs to be split into two
+		//	flow hooks, the first doing the ESP (transport only), and
+		//	the second requesting fragmentation and doing tunnel + AH
+		//	(and possible remaining transforms).
+		}
+	return handle;
+	}
+
+void CIpsecHook::Close()
+	/**
+	* Decrement reference count.
+	*/
+	{
+	CIpsecReferenceCountObject::Close();
+	}
+
+void CIpsecHook::Open()
+	/**
+	* Increment reference count.
+	*/
+	{
+	CIpsecReferenceCountObject::Open();
+	}
+
+TInt CIpsecHook::ReadyL(TPacketHead &aHead)
+	/**
+	* ReadyL implementation for IPsec MFlowHook.
+	*
+	* The ReadyL calls "propagate" interface ready state up the
+	* flow. The calls to hooks are made in reverse order, the
+	* "closest to interface" is called first.
+	*
+	* This function has following tasks
+	*
+	* @li	Verify that all Security Associations (SA) required by the
+	*		actions (CPolicyAction) collected in OpenL, are
+	*		available (call MAssociationManager::Acquire() for
+	*		each).
+	* @li	If any of the required SA's is not yet available, then
+	*		block the flow (and ReadyL) process by returning
+	*		EFlow_PENDING. When the SA's become available, there
+	*		will be a callback (RHookSA::Callback()). which can
+	*		set the flow back to READY state and trigger a rerun
+	*		of the ReadyL chain.
+	*
+	* @param aHead The packet flow information.
+	* @return
+	* 	@li	== 0, hook is ready, proceed to refresh the next one or
+	*		mark the flow as READY, if this was the first hook
+	*	@li	> 0 (EFlow_PENDING), hook is not ready, the ReadyL calling
+	*		is stopped and flow is set to pending state.
+	*	@li	< 0, hook detected a permanent error condition. The flow
+	*		goes into error state.
+	*
+	*  *WARNING*
+	*      The ReadyL method can be called multiple times!
+	*/
+	{
+	TInt result = KErrNone;
+	TInt overhead = 0;
+
+	// ... could ASSERT that current dst/src in packet head is
+	// exactly the same as at the end of the OpenL above.
+	
+	// Restore upper layer state
+	aHead.ip6.SetSrcAddr(iInfo.iLocal());
+	aHead.iSrcId = iInfo.iLocal().iScope;
+	aHead.ip6.SetDstAddr(iInfo.iRemote());
+	aHead.iDstId = iInfo.iRemote().iScope;
+	if (iInfo.iFlags & KTransportSelector_PORTS)
+		{
+		aHead.iSrcPort = iInfo.iPortLocal;
+		aHead.iDstPort = iInfo.iPortRemote;
+		aHead.iIcmpType = 0;
+		aHead.iIcmpCode = 0;
+		}
+	else
+		{
+		aHead.iSrcPort = 0;
+		aHead.iDstPort = 0;
+		aHead.iIcmpType = (TUint8)iInfo.iPortLocal;
+		aHead.iIcmpCode = (TUint8)(iInfo.iPortLocal >> 8);
+		}
+	aHead.ip6.SetVersion(aHead.ip6.DstAddr().IsV4Mapped() ? 4 : 6);
+
+	const RIpAddress *src = &iInfo.iLocal;
+	const RIpAddress *dst = &iInfo.iRemote;
+
+	LOG(Log::Printf(_L("ReadyL\tMFlowHook[%u]"), (TUint)this));
+
+	// Try to acquire the actual SA's required by the bundle
+
+	//
+	// Goes through all SA's, even if some of them fail. This is to
+	// get all possible ACQUIRE requests queued on one packet.
+	//
+	// When entering ready, the head contains the final destination
+	// address as defined by possible tunnels. However, this can be
+	// ignored, as we have the saved original destination which should
+	// be in the packet head after this ReadyL. (And the final destionation
+	// should be original destination or the last tunnel from the bundle.
+	TInt j = 0;
+	TInt tunnel_mode = 0;
+	RPolicySelectorInfo info = iInfo;
+	for (TInt i = 0; i < iCount; ++i)
+		{
+		RHookSA &h = Assoc(i);
+
+		// Save current dst/src for the case of optional item skipped.
+		const RIpAddress *const dst_opt = dst;
+		const RIpAddress *const src_opt = src;
+
+		if (!h.iItem->iTunnel().IsNone())
+			{
+			if (!KOuterIsInner.IsEqual(h.iItem->iTunnel()))
+				dst = &h.iItem->iTunnel;	// destination changed
+			src = &Tunnel(j++);				// Outer Source Address!
+			tunnel_mode = 1;
+			}
+		else
+			tunnel_mode = 0;
+
+		CSecurityAssoc *sa = NULL;
+		if (h.iItem->iSpec == NULL)
+			h.iState = KErrNone;	// Always ready, no SA needed.
+		else
+			{
+			// Tranformation needs an IPSEC Security Association
+			// Because ReadyL can be called multiple times, it is
+			// possible to have associations already setup at this
+			// point (or only some of them not available yet).
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
+			if(h.iItem->iSpec->iPropList->Count() > MAX_PROPOSALS_PER_SA)
+				{
+				User::Leave(EIpsec_MaxTransforms);
+				}
+			TInt res2 = iMgr.Acquire(sa, h.iItem->iSpec->iSpec, h.iItem->iSpec->iPropList, h.iItem->iTS, *src, *dst, info, tunnel_mode); 
+#else
+			TInt res2 = iMgr.Acquire(sa, h.iItem->iSpec->iSpec, h.iItem->iTS, *src, *dst, info, tunnel_mode);
+#endif // SYMBIAN_IPSEC_VOIP_SUPPORT
+			// Note: Acquire returns
+			//  - KErrNone = EFlow_READY, when SA already exists and is usable
+			//  - KRequestPending, when a larval SA is created and keymanagement Acquire in progress
+			//  - KErrNotFound, when SA cannot be allocated for some reason (fatal error, abort flow!)
+			if (res2 == KRequestPending)
+				{
+				// The following test is temporary work-around for a
+				// bug in PFKEY implementation: the PKFEY does not properly
+				// check whether the SA reply to ACQUIRE matches request. It
+				// just changes the matching egg SA into MATURE state.
+				// If above acquire returns pending (no matching SA exists),
+				// and we already have a "MATURE" SA assigned, then assume
+				// the key manager is not providing a correct SA for the ACQUIRE.
+				// To prevent continous ACQUIRE looping, abort flow.
+				const CSecurityAssoc *const sa2 = h.Association();
+				if (sa != sa2 && sa2 && sa2->State() == SADB_SASTATE_MATURE)
+					res2 = EIpsec_AcquireFailed;
+				else
+					res2 = EFlow_PENDING;
+				}
+			h.iState = res2;       // Remember the last result for SA.
+#ifdef _LOG
+			if (res2 > 0)
+				LogBundleItem(_L("\t\tWaiting SA: "), *h.iItem, sa);
+			else if (res2 == 0)
+				LogBundleItem(_L("\t\tSA ready: "), *h.iItem, sa);
+			else
+				LogBundleItem(_L("\t\tSA unavailable: "), *h.iItem, sa);
+#endif
+			//
+			// Keep the first error as final error... however, fatal error
+			// state ( < 0) should override any pending error state ( > 0).
+			//
+			if (h.iItem->iOptional)
+				{
+				if (res2 != KErrNone)
+					{
+					// Optional item not ready, cancel address changes.
+					// (this is not 100% correct--now the following
+					// SA's are requested with the old addresses, and
+					// will become incorrect, if the optional SA appears
+					// at some later stage without ReadyL being rerun!)
+					dst = dst_opt;
+					src = src_opt;
+					tunnel_mode = 0;
+					}
+				}
+			else 
+				{
+				// Only obligatory items affect final result.
+				if (result == KErrNone || (result > 0 && res2 < 0))
+					result = res2;
+				}
+			// For after tunnel, the pktinfo must be changed too..
+			if (tunnel_mode)
+				{
+				info.iRemote = *dst;
+				info.iLocal = *src;
+				AfterTunnelAction(info);
+				}
+			}
+		//
+		// Whatever the result was, attach the new SA (possibly NULL or same as before)
+		// to the handle.
+		//
+		h.Reset(sa);
+		//
+		// Find out how much overhead this transform adds to a packet
+		//
+		overhead += iMgr.Overhead(sa, h.iItem->iTunnel());
+		}
+
+	if (result == KErrNone)
+		{
+		// If all SA's are available, the header space requirement
+		// is now known (coded in a way that allows this to change
+		// dynamicalle, if required)
+		iFlow.iHdrSize += overhead;
+		}
+	return result;
+	}
+
+TInt CIpsecHook::ApplyL(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo)
+	/**
+	* Apply the IPsec actions to the outgoing packet.
+	*
+	*  This is part of IPv6 outgoing packet hook processing.
+	* @li	aPacket is the outgoing packet. It is *FULL* packet
+	*		and already includes the outgoing IPv6 header. If
+	*		the hook needs to do any modifications to the outgoing
+	*		packet, it must make them into aPacket.
+	* @li	aPacket has an info block associated. The info->iLength
+	*      *MUST* be maintained, if any change affects it.
+	*
+	* @param aPacket	The IP packet to process (unpacked).
+	* @param aInfo		The unpacked info block associated with the aPacket.
+	*
+	* @return (as specified for the hook mechanism)
+	* @li   Any Leave causes the packet to be dropped
+	* @li  = 0, pass on to the next output hook
+	* @li  < 0, the hook took "ownership" of the packet (usually
+	*      just dropped it explicitly), "Send aborted" return!
+	* @li  > 0, This return value is not used at this
+	*      point. Do not return with > 0!
+	*/
+	{
+	TInt tunnel = 0;
+	const TIpAddress *dst = &iInfo.iRemote();
+	for (TInt i = 0; i < iCount; ++i)
+		{
+		const RHookSA &h = Assoc(i);
+
+		// Save current dst for the case of optional item skipped.
+		// (src does not need to be saved, because for non-tunnels,
+		// the source is already in the packet, and for tunnels,
+		// the source is always taken from the Tunnel(x). And even
+		// the saved dst is only needed for the case where outer tunnel
+		// dst is defined to be same as inner).
+		const TIpAddress *const dst_opt = dst;
+
+		const TIpAddress *tunnel_address = &h.iItem->iTunnel();
+		if (!tunnel_address->IsNone())
+			{
+			// Pass outer src in the info
+			TInetAddr::Cast(aInfo.iSrcAddr).SetAddress(Tunnel(tunnel++)());
+			// [should also set aInfo.iDstAddr ?]
+		
+			// Use the previous dst as tunnel address, if tunnel is defined as all-ones
+			if (KOuterIsInner.IsEqual(*tunnel_address))
+				tunnel_address = dst;
+			else
+				dst = tunnel_address;
+			}
+		// *NOTE*
+		//  The IPSEC engine ApplyL does not follow the hook Apply return values
+		//  fully. It never reseases the packet and error return is just a signal
+		//  that transformation failed. Perhaps should do something? For now,
+		//  just convert error return to leave! -- msa
+		if (h.iState == KErrNone)
+			User::LeaveIfError(iMgr.ApplyL(h.Association(), aPacket, aInfo, *tunnel_address));
+		else if (!h.iItem->iOptional)
+			User::Leave(EIpsec_LostSA);
+		else
+			dst = dst_opt;
+		//
+		// ApplyL may notice that SA is DYING or already dead, when DYING the flow
+		// should be set to pending after this packet (and renegotiation of SA
+		// sould be activated). If DEAD, the same happens, but additionally the
+		// packet is dropped.
+		}
+	return KErrNone;
+	}
+
+// CProtocolSecpol::UpdateTunnelInterface
+// **************************************
+// The problem: when applying IPSEC detunneling, the inner source and destination
+// addresses must be acceptable for the "inbound interface". When VPN tunnels are
+// in use, the addresses are normally not valid for the real interface and IPsec
+// must update the iInterfaceIndex to point to the proper VPN interface, so that
+// the checks pass.
+//
+// Just using the inner destination and searching for any interface that would
+// accept packets for that address only work for unique addresses. If there are
+// overlapping ranges (10-nets) or IPv6 scoped addresses (site or link local),
+// then the scope id is required.
+//
+// Assume CSecurityAssoc::TunnelIndex() must return the interface index of the
+// associated VPN tunnel interface, if non-zero. (this does not care how
+// such thing got associated with the SA...)
+void CProtocolSecpol::UpdateTunnelInterface(RMBufRecvInfo &aInfo, const CSecurityAssoc *const aSa)
+	{
+	if (aSa && aSa->TunnelIndex())
+		aInfo.iInterfaceIndex = aSa->TunnelIndex();
+	}
+
+void CProtocolSecpol::CheckPacketId(RMBufHookPacket &aPacket)
+	/**
+	* Detect when packet has changed.
+	*
+	* The IPsec hook can get multiple calls from the same packet
+	* to various hooks that it has installed, and they collect state
+	* information into the member variables of the CProtocolSecpol.
+	*
+	* The state information contains
+	* - IPsec transforms done
+	* - Fragment information 
+	*
+	* Sometimes, some other hook or component just throws the packet
+	* away. IPsec needs to reset the collected information, if packet
+	* is changed.
+	*
+	* The network layer tags each packet with id, which can be used
+	* for this purposed (RMBufRecvInfo::HookValue(0)).
+	*/
+	{
+	const TUint32 packet_id = aPacket.HookValue(0);
+	ASSERT(packet_id);			// The packet id must be present!
+	if (packet_id == iPacketId)
+		return;
+	
+	// A new packet is being processed, scrap previous
+	// SA and tunnel information.
+	if (iIsFragment)
+		{
+		CIpsecFragmentInfo **h = &iFrags;
+		CIpsecFragmentInfo *f;
+		
+		// Count the number of currently stored states,
+		// and release all that are over the maximum
+		// count (from the end of chain, oldest ones first).
+		for (TInt count = 0; (f = *h) != NULL;)
+			{
+			if (++count >= KIpsecMaxFragmentInfo)
+				{
+				// Too many Fragment states stored, delete
+				// the entry
+				*h = f->iNext;
+				delete f; 
+				}
+			else
+				h = &f->iNext;
+			}
+		// The other packet was a fragment, need to save
+		// the information for later use. Adds the new
+		// information in front of the list.
+		f = CIpsecFragmentInfo::New(iCountSA);
+		if (f)
+			{
+			f->iId = iId;
+			f->iSrc = iSrc;
+			f->iDst = iDst;
+			for (TInt i = 0; i < f->iCount; ++i)
+				{
+				// Copy a reference to the SA
+				(*f)[i].iSA.Reset(iSA[i].Association());
+				(*f)[i].iTunnel = iTunnel[i]();
+				}
+			f->iNext = iFrags;
+			iFrags = f;
+			}
+		}
+	iPacketId = packet_id;
+	iCountSA = 0;
+	iIsFragment = 0;
+	iIsTunnelMode = 0;
+	}
+	
+TInt CProtocolSecpol::CheckFragmentPolicy()
+	/**
+	* Verify fragment policy.
+	*
+	* A fragment has been detected. Need to check that if there are other fragments
+	* of the same packet, they all have been protected with the same IPsec.
+	*/
+	{
+	CIpsecFragmentInfo **h = &iFrags;
+	CIpsecFragmentInfo *f;
+	for (;(f = *h) != NULL; h = &f->iNext)
+		{
+		if (f->iId != iId || f->iSrc != iSrc || f->iDst != iDst)
+			continue;
+
+		// A matching fragment information already exists in the
+		// list. This means that we have already received other
+		// fragment(s) of this packet and now must check that all
+		// applied IPSEC is same for all fragments.
+		for (TInt i = 0; i < f->iCount; ++i)
+			{
+			if ((*f)[i].iSA.Association() != iSA[i].Association() ||
+				(*f)[i].iTunnel != iTunnel[i]())
+				{
+				// A mismatch has been detected. Either this or previous
+				// fragment had invalid IPSEC applied. There is no way
+				// to know which one, thus just drop the current.
+				return EIpsec_FragmentMismatch;
+				}
+			}
+		// All applied IPSEC matches, release the stored information
+		*h = f->iNext;
+		delete f;
+		break;
+		}
+	return KErrNone;
+	}
+
+TInt CProtocolSecpol::TransformL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+	/**
+	* Do IPsec transforms for incoming packet.
+	*
+	* The CProtocolIpsec binds to AH, ESP and UDP (for NAT traversal), but delegates
+	* actual processing to this main class -- CProtocolIpsec::ApplyL() just calls
+	* this TransformL() directly.
+	*
+	* Additionally, CProtocolIpsec hooks the IPv6 Fragment header to detect
+	* IPv6 fragments.
+	*
+	* @li	Recognize the AH and ESP, and unwrap them from the packet,
+	*		and remember the transforms as packet state.
+	* @li	Recognize the special UDP that does the NAT traversal
+	* @li	Recognize fragments that are uncovered by detunneling
+	*
+	* @param aPacket The incoming packet
+	* @param aInfo The packet info.
+	* @return KIp6Hook_PASS, KIp6Hook_DONE or error, depending on conditions.
+	*/
+	{
+	TInt reason = KErrNotSupported;
+	CheckPacketId(aPacket);	// Do the packet id check
+
+	for (;;)
+		{
+		if (!iAssociationManager)
+			break;              // No CProtocolKey ==> Drop Packet!
+		//
+		// A reminder: Nothing says that the same SA cannot apply
+		// multiple times to a packet (although it would be a weird
+		// policy to define). Thus, beware that table may include
+		// multiple pointers to the same SA. However, iProtocolKey->ApplyL
+		// returns an error, if the SA expires and the packet is dropped
+
+		const TUint id = aInfo.iProtocol;
+		const TInt is_udp = (id == KProtocolInetUdp);
+
+		if (aInfo.iIcmp)
+			{
+			// An ICMP Error report, where the returned packet contains
+			// ESP or AH header supposedly generated by this host...
+			if (is_udp)
+				return KIp6Hook_PASS;
+			reason = EIpsec_IcmpError;
+			break;          // Not much one can do...
+			}
+
+		if (id == KProtocolInetEsp)
+			// There will be ESP processing, for temporary security fix,
+			// prevent this packet from being used in ICMP Error
+			// reports -- msa
+			aInfo.iFlags |= KIpNeverIcmpError;
+		else if (id == KProtocolInetIpip)
+			{
+			// Extract Fragment information from the IPv4 IP Header
+			TInet6Packet<TInet6HeaderIP4> ip(aPacket, aInfo.iOffset);
+			if (ip.iHdr == NULL)
+				{
+				reason = EIpsec_CorruptPacketIn;
+				break;	// Drop! (packet too short)				
+				}
+			if (ip.iHdr->MF() != 0 || ip.iHdr->FragmentOffset() != 0)
+				{
+				iIsFragment = 1;
+				iId = ip.iHdr->Identification() | (ip.iHdr->Protocol() << 16);
+				// What should the scope id be?
+				iSrc.SetAddress(ip.iHdr->SrcAddr());
+				iDst.SetAddress(ip.iHdr->DstAddr());
+				reason = CheckFragmentPolicy();
+				if (reason < 0)
+					break;
+				return KIp6Hook_PASS;
+				}
+			return KIp6Hook_PASS;
+			}
+		else if (id == KProtocolInet6Fragment)
+			{
+			// Extract Fragment information from the IPv6 Fragment Header
+			TInet6Packet<TInet6HeaderFragment> fh(aPacket, aInfo.iOffset);
+			if (fh.iHdr == NULL)
+				{
+				reason = EIpsec_CorruptPacketIn;
+				break;	// Drop! (packet too short)				
+				}
+			iIsFragment = 1;
+			iId = fh.iHdr->Id();
+			iSrc.SetAddress(aInfo.iSrcAddr);
+			iDst.SetAddress(aInfo.iDstAddr);
+			reason = CheckFragmentPolicy();
+			if (reason < 0)
+				break;
+			return KIp6Hook_PASS;
+			}
+
+		if (iCountSA == KIpsecMaxNesting)
+			{
+			reason = EIpsec_MaxTransforms;
+			break;              // Too deep nesting!!!
+			}
+
+		iMyself[iCountSA].Set(TInetAddr::Cast(aInfo.iDstAddr));
+		CSecurityAssoc *sa = NULL;
+		TIpAddress tunnel;
+		reason = iAssociationManager->ApplyL(sa, aPacket, aInfo, id, tunnel);
+		iTunnel[iCountSA].Set(tunnel, tunnel.iScope);
+
+		if (reason == EIpsec_NotANATTPacket)
+			return KIp6Hook_PASS;	// The packet was a normal UDP packet, pass through
+		else if (reason < 0)
+			{
+			//
+			// ApplyL wants the packet dropped
+			//
+			break;
+			}
+		//
+		// If id (= protocol) was UDP, do not increment iCountSA
+		// The current packet has been an UDP capsulated ESP packet from
+		// which the UDP header is now been removed
+		if (!is_udp)
+			{
+			// If detunneling happened, there is a need to change
+			// the interface index based on inner destination! -- msa
+			if (!iTunnel[iCountSA]().IsNone())
+				{
+				UpdateTunnelInterface(aInfo, sa);
+				iIsTunnelMode = 1;		// (should actually take this flag from the SA --msa)				
+				};
+			iSA[iCountSA++].Reset(sa);	// Attach the returned sa to the handle
+			}
+		//
+		// The packet has been changed, report this to the
+		// caller.
+		aInfo.iProtocol = reason;
+		return KIp6Hook_DONE;
+		}
+	//
+	// A Packet is dropped, deliver a copy to potentially interested
+	// parties (should have some limiter here?)
+	//
+	//
+	// *NOTE*
+	//  The reason for the packet drop is now passed in the Port
+	//  field of the info.iSrcAddr. Somewhat kludgy... --msa
+	//
+	iCountSA = 0;
+	LOG(Log::Printf(_L("IPSEC Transform (%d) failed %d"), aInfo.iProtocol, reason));
+	aInfo.iSrcAddr.SetPort(reason);
+	Deliver(aPacket);
+	aPacket.Free();
+	return reason;
+	}
+
+TInt CProtocolSecpol::ApplyL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo)
+	/**
+	* Peek at the packet before "upper layer".
+	*
+	* The Secpol installs a hook that request the stack to show each packet
+	* to IPsec just before it is given to the final upper layer protocol,
+	* such as UDP and TCP, but also any other protocol which is not
+	* handled as an extension header. Note that this includes IP-in-IP
+	* tunneling protocols.
+	*
+	* The method compares an incoming packet and accumulated IPsec state
+	* against the security policy and decides whether the packet is to be
+	* allowed in.
+	*
+	* @param aPacket The packet in "unpacked" state
+	* @param aInfo The packet information block
+	* @return As specified for the hook mechanism
+	*
+	* -	any Leave causes packet to be dropped
+	* -	return < 0, the hook took "ownership" of the packet (usually just dropped it explicitly)
+	* -	return == KIp6Hook_PASS, pass the packet to the next hook
+	*
+	* Note, the return "KIp6Hook_DONE" is not used by Secpol here.
+	*/
+	{
+	CSecurityPolicy *const policy = Policy();
+	if (policy == NULL)
+		return KIp6Hook_PASS;   // No Policy ==> No security Check, pass all
+		
+	if (iIsTunnelMode)
+		{
+		// This IPSEC on top of IP-tunnel, and detunneling is being delegated to the
+		// IP layer. As it is treated as "transport protocol", IPSEC gets called
+		// for a policy check. But, if the IPSEC is in tunnel mode, the policy check
+		// needs to be delayed until some inner transport header.. 
+		ASSERT(aInfo.iProtocol == KProtocolInetIpip || aInfo.iProtocol == KProtocolInet6Ipip);
+		iIsTunnelMode = 0;
+		return KIp6Hook_PASS;
+		}
+
+	TInt reason = KErrNotSupported;
+	const TUint id = aInfo.iProtocol;
+	TPolicyFilterInfo filter;
+
+	if (!iAssociationManager)
+		User::Leave(reason);     // No CProtocolKey ==> Drop Packet!
+	CheckPacketId(aPacket);
+
+	//
+	// Now is the time to check the whether the accumulated
+	// IPSEC matches the policy...
+	//
+	//  The topmost header in packet now holds the packet that
+	//  should correspond something to which the policy is
+	//  applied.
+	//
+	// For Inbound, Local = Destination address, of the packet
+	//              Remote = Source Address of the packet
+	filter.iIndex = aInfo.iInterfaceIndex;
+	filter.iFlags = KPolicyFilter_INBOUND;
+	iPktInfo.FillZ();
+	// (The addresses in info are already in IPv6 format)
+	iPktInfo.iLocal.Set(TInetAddr::Cast(aInfo.iDstAddr));
+	iPktInfo.iRemote.Set(TInetAddr::Cast(aInfo.iSrcAddr));
+
+	// Retrieve all available selector information from the packet
+	// (requires some layer breaking snooping...)
+	for (const TSnoopHeader *f = snooper; f < snooper_end; ++f)
+		{
+		if (f->iProtocol == id)
+			{
+			TInet6Packet<TUpperLayerSnoop> snoop(aPacket, aInfo.iOffset);
+			const TUpperLayerSnoop *const hdr = snoop.iHdr;
+			if (hdr == NULL)
+				{
+				reason = EIpsec_CorruptPacketIn;
+				goto drop;
+				}
+			switch (f->iSelector)
+				{
+				case KLoadPorts:
+					iPktInfo.iPortRemote = hdr->Port(f->iO1);	// src port is remote port for incoming
+					/* Fall through */
+				case KLoadLocal:
+					iPktInfo.iPortLocal = hdr->Port(f->iO2);	// dst port is local port for incoming
+					iPktInfo.iFlags |= KTransportSelector_PORTS;
+					break;
+				case KLoadBoth:
+					iPktInfo.iPortLocal = hdr->Byte(f->iO2);		// ...load code to lower 8 bits.
+					/* Fall through */
+				case KLoadType:
+					iPktInfo.iPortLocal |= hdr->Byte(f->iO1) << 8;	// ..load type to upper 8 bits.
+					iPktInfo.iPortRemote = iPktInfo.iPortLocal;
+					break;
+				default:
+					break;
+				}
+			break;	
+			}
+		}
+	iPktInfo.iProtocol = (TUint8)id;
+
+	if (aInfo.iIcmp == 0)
+		{
+		//
+		// However, if this call is normal processing call for ICMP, check if the
+		// ICMP is actually an error report. In such case trust the main loop
+		// to call this hook again with iIcmp != 0 (it's a bit delicate, as
+		// the check for ERROR ICMP must *exactly* match the check in the
+		// IP layer, or this will pass ICMP packets that are not allowed by
+		// the policy!!!
+		//
+		if (id == KProtocolInet6Icmp)
+			{
+			if (iPktInfo.iPortLocal < (128<<8))
+				return KIp6Hook_PASS;               // Wait for error processing call!
+			}
+		else if (id == KProtocolInetIcmp)
+			{
+			switch (iPktInfo.iPortLocal >> 8)
+				{
+				case KInet4ICMP_Unreachable:        // Destination unreachable
+				case KInet4ICMP_SourceQuench:       // Source Quench
+				case KInet4ICMP_Redirect:           // Redirect request
+				case KInet4ICMP_TimeExceeded:       // Time Exceeded
+				case KInet4ICMP_ParameterProblem:   // Parameter Problem
+					return KIp6Hook_PASS;           // Wait for error processing call!
+				default:
+					break;
+				}
+			}
+		// Not an ICMP or normal ICMP, do the policy check with current info
+		}
+	for (;;)
+		{
+		CPolicyAction *items[KIpsecMaxNesting];
+		TInt tunnels = 0;
+
+		RPolicySelectorInfo final_info = iPktInfo;
+		const TInt count = CollectBundle(filter, final_info, KIpsecMaxNesting, items, NULL, tunnels);
+		if (count < 0)
+			{
+			reason = count;
+			break;
+			}
+
+		const RIpAddress *dst = &iPktInfo.iLocal;		// initial destination is the inner dst (iLocal of incoming)
+		const RIpAddress *src = &iPktInfo.iRemote;		// initial source is the inner src (iRemote of incoming)
+
+		for (TInt j = 0; j < count; j++)
+			{
+			const CPolicyAction &item = *items[j];
+
+			// Need to save current src/dst in case optional
+			// processing is done (only pointers saved).
+			const RIpAddress *const dst_opt = dst;
+			const RIpAddress *const src_opt = src;
+
+
+			if (--iCountSA < 0)
+				{
+				reason = EIpsec_TooFewTransforms;
+				goto optional;
+				}
+			//
+			// Verify that the tunnel address (if any) matches with
+			// the policy entry.
+			if (!item.iIsTunnel)
+				{
+				if (!iTunnel[iCountSA]().IsNone())
+					{
+					reason = EIpsec_TunnelMismatch;
+					goto optional;
+					}
+				}
+			else
+				{
+				//
+				// If a tunnel is specified, all remaining SA's have the src
+				// address of the this tunnel
+				//
+				if (item.iTunnel().IsNone())
+					src = &iTunnel[iCountSA];	// Match tunnel from any source.
+				else if (!KOuterIsInner.IsEqual(item.iTunnel()))
+					src = &item.iTunnel;		// == Outer src
+
+				// From the result of TAHI Tunnel test, there is some reason that cause the 
+				// "src" not eual to iTunnel[iCountSA] but equal to iMyself[iCountSA], that 
+				// will leads to "EIpsec_TunnelMismatch" error in original code.
+				if (iTunnel[iCountSA]() != (*src)())
+					{
+					if (iMyself[iCountSA]() != (*src)())
+					    {
+					    reason = EIpsec_TunnelMismatch;
+					    goto optional;
+					    }
+					src = &iTunnel[iCountSA];   //set "src" to tunnel address
+					}
+				dst = &iMyself[iCountSA];
+				}
+
+			if (item.iSpec)
+				{
+				// An IPSEC SA is required
+				if (iSA[iCountSA].Association() == NULL)
+					{
+					//
+					// SA was present when the AH or ESP was processed, but now appears to have
+					// disappeared. Expired, because it was used in two transforms, and latter
+					// expired? I don't expect to see this error, but just in case... -- msa
+					//
+					reason = EIpsec_LostSA;
+					goto drop;
+					}
+#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT					
+				else if ((reason = iAssociationManager->Verify(iSA[iCountSA].Association(), item.iSpec->iSpec,item.iSpec->iPropList, *src, *dst, iPktInfo)) == KErrNone)
+#else
+				else if ((reason = iAssociationManager->Verify(iSA[iCountSA].Association(), item.iSpec->iSpec, *src, *dst, iPktInfo)) == KErrNone)
+#endif // SYMBIAN_IPSEC_VOIP_SUPPORT			
+					{
+					if (item.iIsTunnel)
+						{
+						// If it was a tunnel mode, need update the packet specific
+						// information for the remaining SA's.
+						iPktInfo.iRemote = *src;
+						iPktInfo.iLocal = *dst;
+						AfterTunnelAction(iPktInfo);
+						}
+					continue;
+					}
+				}
+			else if (iSA[iCountSA].Association())
+				{
+				// Note: Above test may not work in general: if the packet contains two
+				// IPSEC layers using the same SA, and if the later application expires
+				// the SA, the iAssociation member of the first one disappears too!!
+				// [in such case, instead of reporting unrequired SA, it would just
+				//  accept it--probably not a serious mishap...]
+				// However, would not get this far? Expiry would drop the packet at
+				// apply phase or the other SA would not verify anyway? -- msa
+				reason = EIpsec_UnrequiredSA;
+				}
+			else
+				continue;
+
+optional:	// Only gets here in case there is a mismatch. If
+			// the item is optional, then retest the current
+			// with the next item.
+			if (!item.iOptional)
+				goto drop;
+			//
+			// Restore to same state for the next item
+			//
+			++iCountSA;
+			dst = dst_opt;
+			src = src_opt;
+			}
+#ifdef __WINS__
+		if (iCountSA > 0)
+#else /*__WINS__*/
+		if ((KIPsecTunnelInTunnel == 0) && (iCountSA > 0))
+#endif /*__WINS__*/ 
+			{
+			reason = EIpsec_TooManyTransforms;
+			break;
+			}
+#ifndef __WINS__
+		//This is for logging only would be removed during MCL submission
+		else if(KIPsecTunnelInTunnel == 1)
+			{
+			//This is for logging only would be removed during MCL submission
+			LOG(Log::Printf(_L("\nKIPsecTunnelInTunnel=1 enabled")));
+			}
+#endif /* __WINS__*/
+		// *NOTE*
+		//  There is no need to detach the SA's from the handles. Handle does
+		//  not prevent SA from being deleted! Just clearing iCountSA is OK!
+		//
+		return KIp6Hook_PASS;
+		}
+drop:
+	// A Packet is dropped, deliver a copy to potentially interested
+	// parties (should have some limiter here?)
+	//
+	//
+	// *NOTE*
+	//  The reason for the packet drop is now passed in the Port
+	//  field of the info.iSrcAddr. Somewhat kludgy... --msa
+	//
+	LOG(LogSelectorInfo(_L("incoming: "), iPktInfo, filter));
+	LOG(Log::Printf(_L("\tpolicy check failed %d"), reason));
+
+	iCountSA = 0;
+	aInfo.iSrcAddr.SetPort(reason);
+	Deliver(aPacket);
+	aPacket.Free();
+	return reason;
+	}
+
+//
+
+CServProviderBase* CProtocolSecpol::NewSAPL(TUint aSockType)
+	/**
+	* Create a new instance of CProviderSecpol for a socket opened by an application.
+	*
+	* This socket can be used to load the security policy and to listen
+	* what packets are dropped due to current policy.
+	*
+	* This is called from socket server.
+	*
+	* @param aSockType Must be KSockRaw
+	* @return new CProviderSecpol instance.
+	*/
+	{
+	if (aSockType!=KSockRaw)
+		User::Leave(KErrNotSupported);
+	CProviderSecpol *pSAP = new (ELeave) CProviderSecpol(*this);
+	iSAPlist.AddLast(*pSAP);
+	return pSAP;
+	}
+
+void CProtocolSecpol::FixupInterfaceIndexes(CSecurityPolicy *aPolicy)
+	{
+	/**
+	* Complete the interfaces with index.
+	*
+	* If the policy contains interface specific policies, then
+	* one must at this point convert the interface names into
+	* actual interface indexes.
+	*/
+	if (aPolicy && aPolicy->iInterfaces && NetworkService())
+		{
+		// First, find the real interface indexes
+		for (CSelectorInterface *si = aPolicy->iInterfaces; si != NULL; si = si->iNext)
+			{
+			si->iInterfaceIndex = GetInterfaceIndex(*si->iName);
+			}
+		}
+	}
+
+TInt CProtocolSecpol::SetPolicy(const TDesC &aPolicy, TUint &aOffset)
+	/**
+	* Replace the current security policy database with a new one.
+	*
+	* Called from CProviderSecpol::Write(). The tasks are
+	*
+	* @li	Convert the policy definition string into internal format (CSecurityPolicy)
+	*		using the parser CSecurityPolicy::SetPolicy().
+	* @li	Complete the CPolicyInterface objects with indexes (FixupInterfaceIndexes()).
+	* @li	The policy parser creates the algorithm map (CAlgorithmList) as a "side effect".
+	*		Pass the new algorithm map to the MAssociationManager::SetAlgorithms().
+	* @li	Notify stack that all flows must be re-negotiated (MNetworkService::SetChanged()).
+	*
+	* @param aPolicy	The new security policy
+	* @retval aOffset	Current parsing point (also starting point initially)
+	* @return KErrNone, if success and < 0 otherwise
+	*
+	* In case of error, the aOffset indicates the position in
+	* aPolicy at which the parsing terminated.
+	*/
+	{
+	if (iAssociationManager == NULL)
+		{
+		LOG(Log::Printf(_L("CProtocolSecpol::SetPolicy -- cannot load because PFKEY is not running")));
+		return KErrNotReady;
+		}
+
+	const TInt res = CSecurityPolicy::SetPolicy(iPolicy, aPolicy, aOffset, iAssociationManager->EndPointCollection());
+	if (res != KErrNone)
+		return res;
+
+	if (iPolicy == NULL)
+		{
+		LOG(Log::Printf(_L("New policy is empty")));
+		return KErrNone;
+		}
+	else
+		{
+		FixupInterfaceIndexes(iPolicy);
+#ifdef _LOG
+		Log::Printf(_L("New policy:"));
+		iAssociationManager->EndPointCollection().LogPrint(_L("ep\t%S = { %S }"));
+		TInt i = 0;
+		for (CPolicySelector *ps = iPolicy->iSelectors; ps != NULL; ps = ps->iNext)
+			{
+			const TInt N = ps->iActions.Count();
+			TBuf<6> seq;
+			seq.AppendFormat(_L("%3d.\t"), ++i);
+			LogPolicySelector(seq, *ps);
+			if (ps->iFilterData & KPolicyFilter_DROP)
+				Log::Printf(_L("\t\t*DROP*"));
+			else if (N == 0)
+				Log::Printf(_L("\t\t*PASS*"));
+			else
+				{
+				for (TInt i = 0; i < N; ++i)
+					LogBundleItem(_L("\t\t"), *ps->iActions[i]);
+				}
+			}
+		Log::Printf(_L("---"));
+#endif
+		}
+
+	// Currently, the algorithm map results as a side effect
+	// from the policy loading operation (it really should be
+	// independent configuration file). Pass the map to the
+	// PFKEY protocol.
+	iAssociationManager->SetAlgorithms(iPolicy->iAlgorithms);
+
+	if (NetworkService())
+		{
+		// New policy has been installed, request rechecking of
+		// all open flows...
+		NetworkService()->SetChanged();
+		}
+
+	return KErrNone;
+	}
+
+void CProtocolSecpol::Deliver(RMBufPacketBase& aPacket)
+	/**
+	* Deliver a copy of dropped packets to policy sockets.
+	*
+	* This method sends a copy of the packet to all currently open Policy sockets.
+	* This is called from the CProtocolSecpol ApplyL methods for each packet that
+	* is dropped. A copy of the packet is made for each open policy socket and
+	* delivered by a Deliver method of the SAP (see CProviderSecpol::Deliver).
+	*
+	* An application that opens a policy socket, but is not reading it, should close
+	* the input side (Shutdown), so that the queue of the dropped packets will
+	* not waste memory resources in the kernel.
+	*
+	* @param aPacket The packet to be dropped
+	*/
+	{
+	CProviderSecpol* sap;
+	TDblQueIter<CProviderSecpol> iter(iSAPlist);
+
+	while (sap = iter++, sap != NULL)
+		if (sap->IsReceiving())
+			{
+			RMBufRecvPacket copy;
+			TRAPD(err, aPacket.CopyL(copy); aPacket.CopyInfoL(copy));
+			if (err == KErrNone)
+				{
+				copy.Pack();
+				sap->Deliver(copy);
+				}
+			// Can allways call free (if Deliver() took the packet, this is NOP)
+			copy.Free();
+			}
+	}