// Copyright (c) 2005-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:
// sadb.cpp - IPv6/IPv4 IPSEC security association database
//

#include <networking/pfkeyv2.h>
#include "sadb.h"
#include "sa_crypt.h"
#include "pfkeymsg.h"
#include <networking/ipsecerr.h>
#include "ipseclog.h"
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
#include "spdb.h"
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT

// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be
// passed as a template parameter
#if defined(__X86GCC__) || defined(__GCCE__)
#define KSecurityAssocTimeoutOffset 256
__ASSERT_COMPILE(KSecurityAssocTimeoutOffset == _FOFF(CSecurityAssoc, iTimeout));
#else
#define KSecurityAssocTimeoutOffset _FOFF(CSecurityAssoc, iTimeout)
#endif

class CSecurityAssocTimeoutLinkage : public TimeoutLinkage<CSecurityAssoc, KSecurityAssocTimeoutOffset>
	/**
	* Relay Timeout callback.
	*
	* This "linkage" receives the timeout callback from RTimeout::iTimeout
	* and relays the event to CSecurityAssoc::TimerExprired() function. 
	*/
	{
public:
	static void Timeout(RTimeout &aLink, const TTime &aNow, TAny *aPtr)
		{
		Object(aLink)->TimerExpired(*(MAssociationManager*)aPtr, aNow);
		}
	};


TLifetime::TLifetime(const struct sadb_lifetime &aLifetime)
	/**
	* Construct a lifetime object from the PF_KEY sadb_lifetime value.
	*
	* @param aLifetime The PFKEY lifetime value.
	*/
	{
	iAllocations = aLifetime.sadb_lifetime_allocations;
	iBytes = aLifetime.sadb_lifetime_bytes;

	// Load the seconds values as is into TTime to be used
	// later in Freeze to convert into a real time stamp.
	iAddtime = aLifetime.sadb_lifetime_addtime;
	iUsetime = aLifetime.sadb_lifetime_usetime;
	}

TLifetime::TLifetime() : iAllocations(0), iBytes(0), iAddtime(0), iUsetime(0)
	/**
	* Default Lifetime initializer.
	*/
	{
	}


void TLifetime::Freeze(TTime &aTime, const TTime &aNow)
	/**
	* Freeze the lifetime.
	*
	* Convert the relative time (aTime) stored in a TTime as seconds
	* into absolute time counting from the given time stamp (aNow).
	*
	* @param aTime	The time to freeze
	* @param aNow	The current time.
	*/
	{
	TInt64 x = aTime.Int64();

	aTime = Time::MaxTTime();
	if (x <= 0)
		// Zero is used to indicate no limit. PFKEY has 64 bit
		// unsigned int values, but EPOC32 64 bit value is signed.
		// if the requested value is large enough to set the sign,
		// treat it the same as 0, use MaxTTime.
		return;
	if (x > aTime.Int64() / 1000000)
		// Conversion to Microseconds would cause overflow, use MaxTTime
		return;
	x *= 1000000;
	if (aTime.Int64() - x < aNow.Int64())
		// Adding x to now would overflow, use MaxTTime
		return;
	// It is safe to add interval to now
	aTime = aNow;
	aTime += TTimeIntervalMicroSeconds(x);
	}

CSecurityAssoc::CSecurityAssoc(const TPfkeyMessage &aMsg) : iTimeout(CSecurityAssocTimeoutLinkage::Timeout)
	/**
	* Construct SA
	*
	* This constructor is only used through the GETSPI and ADD.
	* It creates a larval SA
	*
	*		<base, address, SPI range>
	*		<base, SA, (lifetimes(HS),) address(SD), (address(P)), ...>
	*
	* Only base, address and SPI from SA are handled by the constructor.
	* ADD must call UpdateL() to get the remainging extensions processed!
	*
	* @li	Lifetimes are initialized, but the TIMER is not activated
	*		here, because timer activation may cause an immediate
	*		expiration and deletion of CSecurityAssoc.
	*
	* @param aMsg	The PFKEY message
	*/
	{
	// iBase must always be present!
	iType = aMsg.iBase.iMsg->sadb_msg_satype;
	iState = SADB_SASTATE_LARVAL;
	if (aMsg.iSa.iExt)
		iSPI = aMsg.iSa.iExt->sadb_sa_spi;
	
	// When src_specific is off from the SA spsecification, then
	// the incoming SA has the dst unspecified, and outgoing SA
	// has the src unspecified. Unspecified can be expressed either
	// by explicitly setting the address as unspecified or just
	// leaving the extension out from the PFKEY message. Need to
	// test the presense of extension in both src and dst.
	// (By default everything is unspecified).
	if (aMsg.iDstAddr.iExt)
		{
		iDst = aMsg.iDstAddr.iAddr;
		iInfo.iPortDst = aMsg.iDstAddr.iPort;
		}
	if (aMsg.iSrcAddr.iExt)
		{
		iSrc = aMsg.iSrcAddr.iAddr;
		iInfo.iPortSrc = aMsg.iSrcAddr.iPort;
		}
	if (aMsg.iProxyAddr.iExt)
		{
		iInfo.iSrc = aMsg.iProxyAddr.iAddr;
		}

	// Set some current times
	TTime time_now;
	time_now.UniversalTime();
	iCurrent.iAddtime = time_now;
	iCurrent.iUsetime = Time::NullTTime();	// as iUsed==0
	TLifetime::Freeze(iSoft.iAddtime, time_now);

	// Set the hard lifetime to the default Larval timeout
	iHard.iAddtime = KLifetime_LARVAL_DEFAULT;
	TLifetime::Freeze(iHard.iAddtime, time_now);

	// All Other fields are assumed to contain initial
	// default values (NULL, 0)
	}

CSecurityAssoc::CSecurityAssoc
	(
	const TSecurityAssocSpec &aSpec,
	const RIpAddress &aSrc,
	const RIpAddress &aDst,
	const RAssociationInfo &aInfo
	) : iTimeout(CSecurityAssocTimeoutLinkage::Timeout)
	/**
	* Construct "larval" SA.
	*
	* This constructor is used for the outgoing packet and it creates
	* a special LARVAL EGG SA (SPI == 0). Called only for ACQUIRE
	* processing.
	*
	* @param aSpec The SA template
	* @param aSrc The SA src address
	* @param aDst The SA dst address
	* @param aInfo Additional information
	*/
	{
	//
	// Contrary to PFKEY text, this implementation contains two kinds of
	// "larval" SA's. This constructor creates an "pre-larval" SA, an "egg"
	// which is recognized from the fact that SPI == 0 (this implementation
	// reserves 0 for internal use and never gives it out as an SPI value)
	iType = aSpec.iType;
	iSPI = 0;
	iDst = aDst;
	iSrc = aSrc;
	iInfo = aInfo;

	// Reference objects, if present need to be refcounted on copy
	if (iInfo.iSrcIdentity)
		iInfo.iSrcIdentity->Open();
	if (iInfo.iDstIdentity)
		iInfo.iDstIdentity->Open();

	iState = SADB_SASTATE_LARVAL;

	iFlags = aSpec.iPfs ? SADB_SAFLAGS_PFS : 0;
	iReplayCheck = (aSpec.iReplayWindowLength != 0);
	//
	// Larval egg does not activate algorithm engines
	// (they need to be created by update!)
	iAalg = aSpec.iAalg;
	iEalg = aSpec.iEalg;

	//
	// Set some base times
	//
	TTime time_now;
	time_now.UniversalTime();
	iCurrent.iAddtime = time_now;
	iCurrent.iUsetime = Time::NullTTime();	// as iUsed==0
	//
	// Set the hard lifetime from the specification, soft
	// lifetime is left as initial infinite. Unspecified
	// iLarvalLifetime is a request to use the default
	// timeout (one cannot request 'infinite' for this)
	iHard.iAddtime = aSpec.iLarvalLifetime ?
		aSpec.iLarvalLifetime : KLifetime_LARVAL_DEFAULT;
	TLifetime::Freeze(iSoft.iAddtime, time_now);
	TLifetime::Freeze(iHard.iAddtime, time_now);
	}

TInt CSecurityAssoc::UpdateL(MAssociationManager &aManager, const TPfkeyMessage &aMsg, CIpsecCryptoManager *aLib)
	/**
	* Update SA from PFKEY message.
	*
	* Initialize CSecurityAssoc from TPfkeyMessage. This is a "blind"
	* operation, most fields are just updated unconditionally, it is
	* assumed that the calling function has verified the legality of
	* this operation. (Some basic checks are done)
	*
	* @param aManager The association manager
	* @param aMsg The PFKEY message
	* @param aLib The crypto manager
	*/
	{
	TInt set_hard = 0;

	// Loading Security Association
	if (aMsg.iSa.iExt)
		{
		//
		// If state is LARVAL, need to "defuse" the internal
		// timeout from the HARD lifetime field, if set.
		// (do this only if aMsg doesn't already rewrite the time)
		//
		set_hard = (iState == SADB_SASTATE_LARVAL);
		if (aMsg.iSa.iExt->sadb_sa_state != SADB_SASTATE_MATURE)
			return KErrGeneral;	 // Only MATURE state can be set.
		if (iSPI && iSPI != aMsg.iSa.iExt->sadb_sa_spi)
			return KErrGeneral;	 // Once SPI is set, it cannot be changed!
		iSPI = aMsg.iSa.iExt->sadb_sa_spi;
		iFlags = aMsg.iSa.iExt->sadb_sa_flags;
		iReplayCheck = (aMsg.iSa.iExt->sadb_sa_replay != 0);

		// This information can only be set for new SA, it cannot
		// be changed or updated (applies to all 'selector' info,
		// including Identity.
		if (aMsg.iSrcAddr.iExt)
			{
			iSrc = aMsg.iSrcAddr.iAddr;
			iInfo.iPortSrc = aMsg.iSrcAddr.iPort;
			}
		if (aMsg.iDstAddr.iExt)
			{
			iDst = aMsg.iDstAddr.iAddr;
			iInfo.iPortDst = aMsg.iDstAddr.iPort;
			iInfo.iProtocol = aMsg.iDstAddr.iExt->sadb_address_proto;
			}
		if (aMsg.iProxyAddr.iExt)
			{
			iInfo.iSrc = aMsg.iProxyAddr.iAddr;
			//
			// Do a special hack: if the proxy address portion is unspecified
			// address, but scope id non-zero, then assume the scope id is
			// actually the interface index of the VPN tunnel interface.
			// (NOTE: this is only useful for inbound SA's?)
			if (iInfo.iSrc().IsUnspecified() && iInfo.iSrc().iScope != 0)
				{
				iTunnelIndex = iInfo.iSrc().iScope;
				iInfo.iSrc.Close();
				}
			}
		}

	// Updating identities should probably only be allowed
	// for larval SA's
	if (aMsg.iDstIdent.iExt && iInfo.iDstIdentity == NULL)
		iInfo.iDstIdentity = CIdentity::NewL(aMsg.iDstIdent.iData, aMsg.iDstIdent.iExt->sadb_ident_type);
	if (aMsg.iSrcIdent.iExt && iInfo.iSrcIdentity == NULL)
		iInfo.iSrcIdentity = CIdentity::NewL(aMsg.iSrcIdent.iData, aMsg.iSrcIdent.iExt->sadb_ident_type);

	//	time_now is used in initialize of the IV and
	//	for lifetimes, so declare and initialize it here
	TTime time_now;
	time_now.UniversalTime();

	// Activating crypto engines
	//		but only if neither of them have been activated before.
	//		If already active,just silently ignore the parameters
	//		in the Message.
	//		(the test below leaves NULL/NULL auth/encrypt situation
	//		open to later update!)
	if (aMsg.iSa.iExt && !iAeng && !iEeng)
		{
		iAalg = aMsg.iSa.iExt->sadb_sa_auth;

		if (iAalg)
			{
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT	
			if (iAalg == SADB_AALG_AES_XCBC_MAC)
				{
				iAeng = aLib->NewMacL(iAalg, aMsg.iAuthKey.iData);
				}
			else
				{
#endif // SYMBIAN_IPSEC_VOIP_SUPPORT				
				iAeng = aLib->NewAuthL(iAalg, aMsg.iAuthKey.iData);
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT				
				}
#endif 				
			if (!iAeng)
				User::Leave(KErrNotFound);	// Conf. Error: algorithm not installed!
			}	
		iEalg = aMsg.iSa.iExt->sadb_sa_encrypt;
		if (iEalg)
			{
			iEeng = aLib->NewEncryptL(iEalg, aMsg.iEncryptKey.iData);
			if (!iEeng)
				User::Leave(KErrNotFound);	// Conf. Error: algorithm not installed!
			}
		if (iEeng)
			{
			// NOTE: this must be allocated even if 0 length!
			delete iIV;		// ..just in case...
			iIV = NULL;		// ..just in case
			iIV = HBufC8::NewL(iEeng->IVSize());
			// The initial content of the IV should be some random
			// bit pattern (important thing is to have something that
			// changes, and is not always Zero, thus the time_now is
			// used). [Using uninitialized memory would be a security
			// risk -- it might even contain the key information!!!]
			// (Use of time is a dubious thing, as it might provide
			// information to be used in some other attacks, as it
			// reveals the exact CPU clock time; but it will do for now!)
			TPtr8 iv(iIV->Des());
			TPtr8 fill = TPtr8((TUint8 *)&time_now, sizeof(time_now), sizeof(time_now));
			// Do not use iv.MaxLength(), it may be larger IV size!
			TInt n = iEeng->IVSize();
			while (n > 0)
				{
				if (fill.Length() > n)
					fill.SetLength(n);
				iv.Append(fill);
				n -= fill.Length(); // This loop will hang, if fill.Length() == 0!!
				}
			}
		}

	//	Loading lifetimes
	//		(Done after all "leaving" and error returns, so that initial life
	//		timers won't get overridden by Update, if it fails
	if (aMsg.iHard.iExt)
		{
		iHard = TLifetime(*aMsg.iHard.iExt);
		if (iUsed)
			TLifetime::Freeze(iHard.iUsetime, iCurrent.iUsetime);
		TLifetime::Freeze(iHard.iAddtime, time_now);
		}
	else if (set_hard)
		{
		iHard = TLifetime();
		TLifetime::Freeze(iHard.iAddtime, time_now);
		}
	if (aMsg.iSoft.iExt)
		{
		iSoft = TLifetime(*aMsg.iSoft.iExt);
		if (iUsed)
			TLifetime::Freeze(iSoft.iUsetime, iCurrent.iUsetime);
		TLifetime::Freeze(iSoft.iAddtime, time_now);
		}

	// Load Traffic Selector
	if (aMsg.iTs.iExt)
		{
		iTS.Reset();
		RTrafficSelector dummy;
		for (TInt i = 0; i < aMsg.iTs.iExt->sadb_x_ts_numsel; ++i)
			{
			User::LeaveIfError(iTS.Append(dummy));
			RTrafficSelector &ts = iTS[i];
			const T_sadb_selector &in_ts = aMsg.iTs.Selector(i);
			ts.iProtocol = in_ts.sadb_x_selector_proto;
			ts.iPortSrc = in_ts.iSrc.Port();
			ts.iPortDst = in_ts.iDst.Port();
			User::LeaveIfError(ts.iSrc.Open(aManager.EndPointCollection(), in_ts.iSrc));
			User::LeaveIfError(ts.iDst.Open(aManager.EndPointCollection(), in_ts.iDst));
			}
		}

	// Replace CNatTraversal object to handle ESP UDP encapsulation (created if required)
	delete iNatTraversal;
	iNatTraversal = CNatTraversal::New(iFlags, aMsg.iPrivateExtension);

	// 'set_hard' is only defined when Update wants to change the SA
	// state into MATURE. The actual state change is delayed until
	// here, in case update fails before this. The SA will most likely
	// be unusable, but at least it won't be MATURE either...
	if (set_hard)
		iState = SADB_SASTATE_MATURE;
	return KErrNone;
	}

void CSecurityAssoc::Cleanup()
	/**
	* Release all memory resources attached to a SA.
	*/
	{
	iTimeout.Cancel();
	if (iInfo.iSrcIdentity)
		{
		iInfo.iSrcIdentity->Close();
		iInfo.iSrcIdentity = NULL;
		}
	if (iInfo.iDstIdentity)
		{
		iInfo.iDstIdentity->Close();
		iInfo.iDstIdentity = NULL;
		}

	iTS.Reset();

	delete iIV; iIV = NULL;

	// Detach all handles
	while (!iHandles.IsDetached())
		{
		RSecurityAssociation *handle = (RSecurityAssociation *)iHandles.iNext;
		ASSERT(handle->iAssociation == this);

		 /* Since the iAssociation was never set, so the iErrValue is set over here
	       * which will be used by sc_prt6.cpp to pass the error value
           * from the kmd to the tcp/ip stack
           */
		handle->SetError(iErrValue);
        		
		handle->None();
		(handle->iCallback)(*handle);
		}

	delete iEeng; iEeng = NULL;
	delete iAeng; iAeng = NULL;

	delete iNatTraversal; iNatTraversal = NULL;
	}

CSecurityAssoc::~CSecurityAssoc()
	/**
	* Destructor.
	*/
	{
	Cleanup();
	}

TInt CSecurityAssoc::Close()
	/**
	* Decrement the reference count.
	*
	* @return
	*	@li KErrNone, if association still exists
	*	@li KErrDied, if association has been deleted.
	*/
	{
	if (--iRefs < 0)
		{
		delete this;
		return KErrDied;
		}
	return KErrNone;
	}


TBool RTrafficSelector::operator<=(const RTrafficSelector &aSel) const
	/**
	* The <= comparison for selector values.
	*
	* Every field must fullfull the "<="-condition individually for
	* this to return true. This operator should only be used to
	* compare individual packet values against a min and max
	* range:
	* @code min <= packetdata <= max
	* @endcode
	*
	* @param aSel The selector (packet or max). With 'packet', this = min, and
	* with 'max', this is 'packet'.
	*
	* @see CSecurityAssoc::Match
	*/
	{
	return
		iProtocol <= aSel.iProtocol &&
		iPortSrc <= aSel.iPortSrc &&
		iPortDst <= aSel.iPortDst &&
		iSrc() <= aSel.iSrc() &&
		iDst() <= aSel.iDst();
	}

// This is defined based on the assumption that RArray<>::Reset()/Close()
// does not run destructor on the array elements. Because RTrafficSelector
// holds handles for addresses, they must be closed before the actual
// RArray methods are called.
void RTrafficSelectorSet::Reset()
	{
	/**
	* Release Traffic Selector space
	*/
	TInt i = Count();
	while (--i >= 0)
		{
		(*this)[i].iSrc.Close();
		(*this)[i].iDst.Close();
		}
	RArray<RTrafficSelector>::Reset();
	}


TInt CSecurityAssoc::MatchSpec
	(
	const TSecurityAssocSpec &aSpec,
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT
	const CPropList *aPropList,
#endif	//SYMBIAN_IPSEC_VOIP_SUPPORT	
	const RIpAddress &aSrc,
	const RIpAddress &aDst,
	const RAssociationInfo &aInfo,
	const RTrafficSelector &aPkt
	) const
	/**
	* Compare SA to SA template and per packet information.
	*
	* Test if the SA matches the requirements.
	*
	* @param aSpec	The SA template
	* @param aSrc	The expected SA source address
	* @param aDst	The expected SA destination address
	* @param aInfo	The additional information (PFP information, etc)
	* @param aPkt	The packet information (to check agains TS)
	*
	* @return KErrNone, when a match occurs, or negative error,
	* when some mismatch is detected
	*/
	{
	if (iDst() != aDst())
		return EIpsec_MismatchedDestination;
	if (iSrc() != aSrc())
		return EIpsec_MismatchSource;
	
#ifdef SYMBIAN_IPSEC_VOIP_SUPPORT	
	if (((iFlags & SADB_SAFLAGS_PFS) == 0) != (aSpec.iPfs == 0))
		return EIpsec_MismatchedPFS;
	for (TInt i=0;i<aPropList->Count();i++)
		{
		CSecurityProposalSpec *proposal = aPropList->At(i);
		TInt ret=KErrNone;
	
		if (iType != proposal->iType) ret= EIpsec_MismatchedType;
		if (iAalg != proposal->iAalg) ret = EIpsec_MismatchedAuthAlg;
		if (iEalg != proposal->iEalg) ret = EIpsec_MismatchedEncryptAlg;
		if (ret == KErrNone) break;
		if ( (i == aPropList->Count()-1)&&(ret != KErrNone) ) return ret;
		}
#else
	if (iType != aSpec.iType)
		return EIpsec_MismatchedType;
	if (((iFlags & SADB_SAFLAGS_PFS) == 0) != (aSpec.iPfs == 0))
		return EIpsec_MismatchedPFS;
	if (iAalg != aSpec.iAalg)
		return EIpsec_MismatchedAuthAlg;
	if (iEalg != aSpec.iEalg)
		return EIpsec_MismatchedEncryptAlg;
#endif //SYMBIAN_IPSEC_VOIP_SUPPORT		

	
	// Fail ReplayCheck test only if the SA specification
	// requires replay check, but SA doesn't enable it.
	// But, allow SA to require replay check, even if spec doesn't.
	if (aSpec.iReplayWindowLength && !iReplayCheck)
		return EIpsec_MismatchReplayWindow;

	if (iInfo.iProtocol != aInfo.iProtocol)
		return EIpsec_MismatchProtocol;
	if (iInfo.iPortSrc != aInfo.iPortSrc)
		return EIpsec_MismatchSourcePort;
	if (iInfo.iPortDst != aInfo.iPortDst)
		return EIpsec_MismatchDestinationPort;
	
	// The "proxy" of PKFEY is stored in iInfo.iSrc of SA
	if (iInfo.iSrc() != aInfo.iSrc())
		return EIpsec_MismatchProxy;
	if (iInfo.iDst() != aInfo.iDst())
		return EIpsec_MismatchProxy;
	
	// *NOTE*
	//		Identities need to be in aInfo, because TSecurityAssocSpec can
	//		be shared between inbound and outboud, and identities are expressed
	//		as remote and local. The caller knowing the context must copy the
	//		pointers into RAssociationInfo properly!
	//
	//		As identity blocks are shared, do raw pointer compare first, it should
	//		optimize at least the processing of outbound SA's (as for those, the
	//		pointers are just references to the identities in TSecurityAssocSpec)!
	//
	if (iInfo.iDstIdentity != aInfo.iDstIdentity &&
		(iInfo.iDstIdentity == NULL ||		// No match if, either is NULL
		 aInfo.iDstIdentity == NULL ||		//
		 !iInfo.iDstIdentity->Match(*aInfo.iDstIdentity)))
		 return EIpsec_MismatchDestinationIdentity;
	if (iInfo.iSrcIdentity != aInfo.iSrcIdentity &&
		(iInfo.iSrcIdentity == NULL ||		// No match if, either is NULL
		 aInfo.iSrcIdentity == NULL ||		//
		 !iInfo.iSrcIdentity->Match(*aInfo.iSrcIdentity)))
		 return EIpsec_MismatchSourceIdentity;
		 
	// Finally, verify the Traffic Selector Match

	TInt i = iTS.Count();
	if (i == 0)
		return KErrNone;	// SA does not have traffic selectors (implicit match all)

	while (i > 1)
		{
		const RTrafficSelector &max = iTS[--i];
		const RTrafficSelector &min = iTS[--i];
		// Is packet info within range?
		if (min <= aPkt && aPkt <= max)
			return KErrNone;
		}

	return EIpsec_MismatchProxy;	// [ Need a new error code for TS mismatch! -- msa]
	}



int CSecurityAssoc::ReplayCheck(TUint32 aSeq)
	/**
	* Check a sequence number against a replay window.
	*
	* Perform the sequence number tracking and replay window
	* checking. After checking the iTestSeq will contain the
	* correct sequence number, and if ESN is used, the high
	* order part is assigned according to the ESN rules.
	*
	* @param aSeq The low order 32 bits of the sequence number
	*
	* @return
	*	- 0, if too old or duplicate,
	*	- 1, if not duplicate or after window
	*/
	{
	iTestInWindow = 0;
	iTestSeq = aSeq;
	iTestSeq.SetHigh(iRecvSeq.High());
	if (iFlags & SABD_SAFLAGS_ESN)
		{
		// Extended Sequence Number enabled. The authentication code
		// requires the correct high order bits, and this code must
		// be executed whether replay check is enabled or not
		//
		// *NOTE* The following is based modulo 2**32 (unsigned) arithmetic!
		//
		const TUint32 lower = iRecvSeq - KMaxReplayWindowLength + 1;
		if (lower <= -KMaxReplayWindowLength)
			{
			if (aSeq >= lower)
				{
				// After window begin
				if (aSeq > iRecvSeq)
					return 1;		// After window end, Check integrity only
				// Inside window, check replay bitmap
				}
			else
				{
				// Before window begin, wrapped around
				iTestSeq.SetHigh(iTestSeq.High() + 1);
				return 1;			// After window, check integrity only
				}
			}
		else
			{
			if (aSeq >= lower)
				{
				iTestSeq.SetHigh(iTestSeq.High() - 1);
				// Inside window, check replay bitmap
				}
			else
				{
				if (aSeq > iRecvSeq)
					{
					return 1;	// Check integrity only
					}
				// Inside window, check replay bitmap
				}
			}
		if (!iReplayCheck)
			return 1;	// ESN without replay check!
		}
	else if (!iReplayCheck)
		{
		// No replay check, Normal Sequence numbers
		return 1;
		}
	else
		{
		// Replay Check, Normal Sequence Numbers
		
		//LOG(Log::Printf(_L("SA[%u] seq=%u:%u"), this, iTestSeq.High(), (TInt)iTestSeq));
		if (aSeq == 0) 
			return 0;			// Zero is illegal sequence number
		if (aSeq > iRecvSeq)	// New larger sequence number
			return 1;
		const TUint32 diff = iRecvSeq - aSeq;
		if (diff >= KMaxReplayWindowLength)
			return 0;			// Too old or wrapped
		}
	iTestInWindow = 1;
	const TInt i = BitmapWord(aSeq);
	const TInt b = BitmapBit(aSeq);
//	LOG(Log::Printf(_L("SA[%u] RC seq=%u:%u iBitmap[%d] bit %d = %d"),
//		this, iTestSeq.High(), (TInt)iTestSeq, i, b, (iBitmap[i] & (1L << b)) != 0));
	return (iBitmap[i] & (1L << b)) == 0;
	}


void CSecurityAssoc::ReplayUpdate(TUint32 aSeq)
	/**
	* Commit/accept the sequence number.
	*
	* This assumes that the ReplayCheck with the same sequence
	* number was called before this. This function can only be
	* called, if the packet authenticates correctly (and thus
	* the sequence number is valid too).
	*
	* @param aSeq	The low order 32 bits of the sequence number.
	*/
	{
	ASSERT(aSeq == iTestSeq);

//	LOG(Log::Printf(_L("SA[%u] RU T=%u:%u"), this, iRecvSeq.High(), (TInt)iRecvSeq));
	if (!iReplayCheck)
		{
		iRecvSeq = iTestSeq;
		return;
		}

	if (!iTestInWindow)
		{
		// Need to adjust window, aSeq is the new leading window edge.
		const TUint32 diff = aSeq - iRecvSeq;
		if (diff < KMaxReplayWindowLength)
			{
			// need to clear bits iRecvSeq -> aSeq
			// brute force loop...
			while (aSeq != ++iRecvSeq)
				{
				const TInt ix = BitmapWord(iRecvSeq);
				const TInt ib = BitmapBit(iRecvSeq);
				iBitmap[ix] &= ~(1L << ib);
				}
			}
		else
			{
			iBitmap[0] = 0;
			iBitmap[1] = 0;
			iBitmap[2] = 0;
			iBitmap[3] = 0;
			iRecvSeq = iTestSeq;
			}
		}
	// *note*
	//	If window is not adjusted, the earlier ReplayCheck already
	//	guarantees that the sequence number is within the window.
	const TInt i = BitmapWord(aSeq);
	const TInt b = BitmapBit(aSeq);
	iBitmap[i] |= (1L << b);
//	LOG(Log::Printf(_L("SA[%u] RU seq=%u:%u iBitmap[%d] bit %d = %d"),
//		this, iTestSeq.High(), (TInt)iTestSeq, i, b, (iBitmap[i] & (1L << b)) != 0));
	}


TInt CSecurityAssoc::TimerInit(MAssociationManager &aManager)
	/**
	* Initialize timer for the SA, if lifetimes require it.
	*/
	{
	TTime now;
	now.UniversalTime();
	return TimerExpired(aManager, now);
	}

TInt CSecurityAssoc::MarkUsed(MAssociationManager &aManager)
	/**
	* SA has been used.
	*
	* MarkUsed is called when the SA has been used for something:
	* Check the count based lifetimes and record the "first use" time.
	*/
	{
	TInt result = CountExpired(aManager);
	if (result == KErrNone && !iUsed)
		{
		//
		// First Use, "freeze" the times
		//
		iUsed = 1;
		iCurrent.iUsetime.UniversalTime();
		TLifetime::Freeze(iHard.iUsetime, iCurrent.iUsetime);
		TLifetime::Freeze(iSoft.iUsetime, iCurrent.iUsetime);
		// Activate Timers (if required)
		result = TimerExpired(aManager, iCurrent.iUsetime);
		}
	return result;
	}


TInt CSecurityAssoc::TimerExpired(MAssociationManager &aManager, const TTime &aNow)
	/**
	* Examine the lifetime expiration.
	*
	* TimerExpired is called when the life status of this SA
	* should be re-examined relative to the current time (aNow).
	*
	* @li *NOTE*
	*		This method just does not only expire. It also
	*		re-activates the timer, if SA is not expired and
	*		has lifetimes based on time. Thus, this is used
	*		internally also to activate the timers for the
	*		first time.
	* @param aManager	Association Manager
	* @param aNow		The current time.
	*
	* @return
	*		KErrNone, if SA did not expire
	*		KErrDied, if SA expired
	*/
	{
	const TTime *time;
	TTimeIntervalSeconds delta;
	// 'inifinite' is the return value from SecondsFrom, and is set non-zero
	// when the time interval in seconds does not fit into 32 bit integer.
	//
	// If the expiration is so far in the future that 32bit integer
	// cannot hold the number of seconds, don't set the timer, just
	// assume no expiration! (I hope nobody sends Psion on a probe
	// to Alpha Centauri with this code, while trying to specify other
	// than infinite lifetime -- msa)
	TInt infinite;


	// Choose the nearest of limits for study. If SA is never used
	// yet, always checks the addtime only.
	time = (!iUsed || iHard.iAddtime < iHard.iUsetime) ?
		&iHard.iAddtime : &iHard.iUsetime;

	if (*time <= aNow)
		{
		// Hard lifetime expired
		iState = SADB_SASTATE_DEAD;
		aManager.Expired(*this, SADB_EXT_LIFETIME_HARD, iHard);
		aManager.Delete(this);
		return KErrDied;
		}
	infinite = time->SecondsFrom(aNow, delta);
	//
	// If SOFT expire has already occurred (DYING), then ignore
	// soft expire times (only generate SOFT Expire message once!)
	//
	if (iState != SADB_SASTATE_DYING)
		{
		// Choose the nearest of limits for study
		time = (!iUsed || iSoft.iAddtime < iSoft.iUsetime) ?
			&iSoft.iAddtime : &iSoft.iUsetime;
		if (*time <= aNow)	// Soft lifetime expired?
			{
			iState = SADB_SASTATE_DYING;
			aManager.Expired(*this, SADB_EXT_LIFETIME_SOFT, iSoft);
			}
		else
			{
			TTimeIntervalSeconds soft;
			if (time->SecondsFrom(aNow, soft) == 0 && (infinite || soft < delta))
				{
				// Non-infinite soft time specified, and is less than
				// than the hard time, use this as a base for the timer
				// setup.
				infinite = 0;	// Not infinite, a value exists.
				delta = soft;
				};
			}
		//
		// DoCallbacks is needed when DYING is set in above,
		// but is placed here, because PFKEY Update/Add message eventually
		// ends up here (via TimerInit) when it changes the state of
		// the SA, and callbacks are needed also then.. -- msa
		if (DoCallbacks() == KErrDied)
			return KErrDied;	// Hups, lost the SA!
		}
	//
	// SA is not dead, reactivate timer, if needed
	//
	if (!infinite)
		aManager.TimerOn(*this, delta.Int() > 0 ? delta.Int() : 1);
	return KErrNone;
	}

TInt CSecurityAssoc::CountExpired(MAssociationManager &aManager)
	/**
	* Examine the count based lifetime.
	*
	* CountExpired is called by when the life status of this SA
	* in respect of bytes or allocations counts should
	* be re-examined.
	*
	* @param aManager The association manager
	* @return
	*	KErrNone, if SA did not expire
	*	KErrDied, if SA expired (not available any more)
	*/
	{
	if ((iHard.iBytes != 0 && iHard.iBytes < iCurrent.iBytes) ||
		(iHard.iAllocations && iHard.iAllocations < iCurrent.iAllocations))
		{
		// Hard lifetime expired
		iState = SADB_SASTATE_DEAD;
		aManager.Expired(*this, SADB_EXT_LIFETIME_HARD, iHard);
		aManager.Delete(this);	// <-- *this* is DELETED!!!
		return KErrDied;
		}
	//
	// If SOFT expire has already occurred (DYING), then ignore
	// soft expire times (only generate SOFT Expire message once!)
	//
	if (iState != SADB_SASTATE_DYING)
		{
		if ((iSoft.iBytes != 0 && iSoft.iBytes < iCurrent.iBytes) ||
			(iSoft.iAllocations && iSoft.iAllocations < iCurrent.iAllocations))
			{
			iState = SADB_SASTATE_DYING;
			aManager.Expired(*this, SADB_EXT_LIFETIME_SOFT, iSoft);
			}
		}
	return KErrNone;
	}


TInt CSecurityAssoc::DoCallbacks()
	/**
	* Call the callbacks registered for the SA.
	*
	* This is called for every state change of SA (except for DEAD,
	* which is handled in the Delete() above)
	*
	* @return Either
	*	KErrNone, if SA instance still exists
	*	KErrDied, if SA instance was destroyed and none other!
	*/
	{
	//
	// *NOTE*
	//		As the callback may delete the handle (and may even
	//		cause deletion of handles before and after, things
	//		get somewhat difficult...
	//
	RCircularList list(iHandles);
	Open();	 // Can't have the SA being pulled from under us!
	while ((list.iNext) != &list)
		{
		RSecurityAssociation *r = (RSecurityAssociation *)list.iNext;
		r->iCallback(*r);
		if (list.iNext == r)
			{
			//
			// The handle was not deleted, move it back to the SA
			//
			r->Detach();
			iHandles.Attach(*r);
			}
		}
	return Close(); // Now, let it go, if someone deleted it!
	}
