// 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();
			}
	}
