diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev1lib/src/ikev1sa.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev1lib/src/ikev1sa.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,471 @@ +/* +* Copyright (c) 2003-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: IKEv1 SA +* +*/ + + +#include "ikedebug.h" +#include "ikev1SA.h" +#include "ikev1SAdata.h" +#include "ikev1keepalive.h" +#include "ikev1nokianattkeepalive.h" // CIkev1NokiaNattKeepAlive +#include "ikepolparser.h" +#include "ikesocketdefs.h" +#include "ikev1pluginsession.h" + +CIkev1SA* CIkev1SA::NewL( CIkev1PluginSession& aPluginSession, + TIkev1SAData& aIkev1SAdata, + CSARekeyInfo* aSaRekey, + MIkeDebug& aDebug ) +{ + CIkev1SA *sa = new (ELeave) CIkev1SA( aPluginSession, aDebug ); + sa->ConstructL( aIkev1SAdata, aSaRekey ); + return sa; +} + + +//Constructor +CIkev1SA::CIkev1SA( CIkev1PluginSession& aPluginSession, + MIkeDebug& aDebug ) + : CTimer( EPriorityStandard ), + iPluginSession( aPluginSession ), + iDebug( aDebug ) +{ + CActiveScheduler::Add(this); +} + +void CIkev1SA::ConstructL(TIkev1SAData& aIkev1SAdata, CSARekeyInfo* aSaRekey) +{ + CTimer::ConstructL(); + iHdr.CopyL(aIkev1SAdata); + + if ( aSaRekey ) + { + // + // Rekeyed IKE SA. Try to find "original" IKE SA and move IPSEC + // SPI list from that SA to the new rekeyed one. + // If "original" IKE SA is found, (re)start expiration timer + // with rekey "left over" time. + // + iRekeyed = ETrue; + CIkev1SA *OrigSA = iPluginSession.FindIkev1SA(aSaRekey->GetCookieI(), aSaRekey->GetCookieR()); + if ( OrigSA ) + { + DEBUG_LOG(_L("ISAKMP SA Rekeyed, SPI list moved from original SA")); + iSPIList = OrigSA->iSPIList; + OrigSA->iSPIList = NULL; + OrigSA->iSPIList = new (ELeave) CIpsecSPIList(1); // Dummy + if ( OrigSA->IsActive() ) + { + OrigSA->Cancel(); + OrigSA->iRemainingTime = 0; + OrigSA->iLeftOverTime = 0; + DEBUG_LOG1(_L("Rekeyed SA expiration time set to %u"),OrigSA->iRemainingTime); + OrigSA->StartTimer(); + } + } + } + + if ( !iSPIList ) + iSPIList = new (ELeave) CIpsecSPIList(4); + + TInt DPDHeartbeat; + + if ( iHdr.iDPDSupported && iHdr.iIkeData->iDPDHeartBeat ) + DPDHeartbeat = iHdr.iIkeData->iDPDHeartBeat; + else DPDHeartbeat = 0; + + TInt KeepAliveTimeout = 0; + TInt port = IkeSocket::KIkePort500; + TUint32 NATKeepAlive = (iHdr.iNAT_D_Flags & LOCAL_END_NAT); + if ( NATKeepAlive || iHdr.iNAT_T_Required ) + { + KeepAliveTimeout = (TInt)iHdr.iIkeData->iNatKeepAlive; + if ( NATKeepAlive ) + { + port = IkeSocket::KIkePort4500; + if ( KeepAliveTimeout == 0 ) + KeepAliveTimeout = 120; // If not configured use 2 minutes + } + } + + if ( DPDHeartbeat || KeepAliveTimeout ) + { + iIkeKeepAlive = CIkeV1KeepAlive::NewL( iPluginSession, + port, + (TInetAddr&)iHdr.iDestinAddr, + KeepAliveTimeout, + DPDHeartbeat, + (MDpdHeartBeatEventHandler*)this ); + } + + // Nokia NAT-T needed + if (!NATKeepAlive && + iHdr.iNAT_T_Required && + (KeepAliveTimeout > 0) ) + { + // Start Nokia IPsec over NAT keepalive handler + TInetAddr addr = (TInetAddr)iHdr.iDestinAddr; + + // NAT-T default ESP UDP port + TInt port(KNokiaNattDefaultPort); + if (iHdr.iIkeData->iEspUdpPort) + port = iHdr.iIkeData->iEspUdpPort; + + iNokiaNatt = CIkev1NokiaNattKeepAlive::NewL( iPluginSession, + addr, + port, + KeepAliveTimeout, + iDebug ); + } + + if ( !iHdr.iVirtualIp && aSaRekey ) + { + // + // Rekeyed IKE SA. No virtual IP address received in IKE SA + // negotiation. Get "old" virtual IP address saved into + // CSARekeyInfo object (if any). + // + iHdr.StoreVirtualIp(aSaRekey->GetInternalAddr()); + } + + + //Lifetime in seconds + iRemainingTime = iHdr.iLifeTimeSecs; + if ( iRemainingTime == 0 ) + iRemainingTime = DEFAULT_MAX_ISAKMP_LIFETIME; + + // + // Check if IKE SA rekeying threshold value (per cent) defined + // If it is (value is between 70 - 95), use that per cent value + // as IKE SA timeout (Rekey for a new IKE SA is started then) + // "Left over" time is the expiration timeout for rekeyed IKE SA + // value which is used when rekey negotiation is started. + // The minimum value for that is set to 30 seconds + // + TInt RekeyThreshold = iHdr.iIkeData->iRekeyingThreshold; + if ( RekeyThreshold != 0 ) + { + if ( RekeyThreshold < 70 ) + RekeyThreshold = 70; + else if ( RekeyThreshold > 95 ) + RekeyThreshold = 95; + DEBUG_LOG1(_L("Negotiated ISAKMP Lifetime set to %u"),iRemainingTime); + iLeftOverTime = iRemainingTime - ((iRemainingTime/100.0) * RekeyThreshold); + iRemainingTime -= iLeftOverTime; + if ( iLeftOverTime < 30 ) + iLeftOverTime = 30; + } + + DEBUG_LOG1(_L("ISAKMP Lifetime set to %u"),iRemainingTime); + + //Lifetime in Kb + iRemainingKB = iHdr.iLifeTimeKB; + DEBUG_LOG1(_L("ISAKMP KB Lifetime set to %u"),iRemainingKB); + + StartTimer(); + +} + +//Destructor +CIkev1SA::~CIkev1SA() +{ + Cancel(); + + //Delete the IPSEC SAs as well if desired + if ( iHdr.iIkeData && iSPIList) + { + for (TInt i = 0; i < iSPIList->Count(); i++) + { + TIpsecSPI* spi_node = iSPIList->At(i); + iPluginSession.DeleteIpsecSA( spi_node->iSPI, + spi_node->iSrcAddr, + spi_node->iDstAddr, + spi_node->iProtocol ); + } + } + + iHdr.CleanUp(); + //Deletes the SPI List + delete iSPIList; + delete iIkeKeepAlive; + delete iNokiaNatt; +} + + +void CIkev1SA::SetExpired() +{ + DEBUG_LOG(_L("CIkev1SA::SetExpired")); + + if ( !iExpired ) //If already expired do nothing to avoid renewing the expiration timer. + { + DEBUG_LOG(_L("SA is still active. Expiring it...")); + + iExpired = ETrue; + //if ( iHdr.iIkeData->iIpsecExpires ) + //{ + //DEB(iEngine->PrintText(_L("iIpsecExpires is ETrue\n"));) + for (TInt i = 0; i < iSPIList->Count(); i++) + { + DEBUG_LOG(_L("Deleting IPsec SA")); + TIpsecSPI* spi_node = iSPIList->At(i); + iPluginSession.DeleteIpsecSA( spi_node->iSPI, + spi_node->iSrcAddr, + spi_node->iDstAddr, + spi_node->iProtocol ); + } + //} + Cancel(); //Cancel the current timer + After(ISAKMP_DELETE_TIME); + } +} + +void CIkev1SA::UpdateSAL(TBool aExpired, TIkev1SAData* aIkev1SAdata) +{ + DEBUG_LOG(_L("CIkev1SA::UpdateSAL")); + + if ( aExpired ) + { + ExpireSA(); + } + else + { + DEBUG_LOG(_L("Not expiring SA")); + if ( aIkev1SAdata ) + { + iHdr.CopyL(*aIkev1SAdata); + } + } +} + +void CIkev1SA::ExpireSA() + { + DEBUG_LOG(_L("Expiring SA")); + SetExpired(); + } + +void CIkev1SA::DoCancel() +{ + CTimer::DoCancel(); +} + +void CIkev1SA::RunL() +{ + + DEBUG_LOG(_L("CIkev1SA::RunL")); + if (!iExpired) //Still alive so that's a normal Lifetime Expiration + { + DEBUG_LOG(_L("Sa is not expired")); + + if (iRemainingTime > 0) //Timer still no finished + { + StartTimer(); + return; + } + + if ( iLeftOverTime ) + { + // + // Start IKE phase 1 rekey operation + // + iRemainingTime = iLeftOverTime; + iLeftOverTime = 0; + CSARekeyInfo* SARekeyInfo = CSARekeyInfo::NewL(iHdr.iCookie_I, iHdr.iCookie_R, iHdr.iVirtualIp); + + iHdr.iVirtualIp = NULL; //Exclusive ownership of the object moved to TSARekeyInfo + DEBUG_LOG(_L("Starting ISAKMP SA rekeying ")); + CleanupStack::PushL(SARekeyInfo); + iPluginSession.RekeyIkeSAL(&iHdr, SARekeyInfo); + CleanupStack::Pop(SARekeyInfo); + StartTimer(); + } + else + { + DEBUG_LOG(_L("**\n---ISAKMP SA Deleted---- Lifetime expired**")); + iPluginSession.DeleteIkeSA(&iHdr, EFalse); // "Normal" close + SetExpired(); + } + } + else + { //Expired must be erased Completely after the default waiting time + + DEBUG_LOG(_L("Deleting IKE Sa")); + iPluginSession.RemoveIkeSA( this, iStatus.Int() ); + } + +} + +TInt CIkev1SA::RunError(TInt aError) + { + DEBUG_LOG1(_L("CIkev1SA::RunError, err=%d"), aError); + iPluginSession.HandleError(aError); + return KErrNone; + } + +void CIkev1SA::StartTimer() +{ + if (iRemainingTime > KMaxTInt/SECOND) //To avoid overflowing the Timer + { + iRemainingTime -= KMaxTInt/SECOND; + After(KMaxTInt); + } + else //No overflow + { + After(iRemainingTime*SECOND); + iRemainingTime = 0; + } +} + +//Adds a new node to the List of SPIs to know the direction if it has to be deleted. +void CIkev1SA::AddIpsecSPIL(TIpsecSPI& aIpsecSpi) +{ + TIpsecSPI* spi_node = new (ELeave) TIpsecSPI; + CleanupStack::PushL(spi_node); + iSPIList->AppendL(spi_node); + CleanupStack::Pop(); + spi_node->iSrcAddr = aIpsecSpi.iSrcAddr; + spi_node->iDstAddr = aIpsecSpi.iDstAddr; + spi_node->iSPI = aIpsecSpi.iSPI; + spi_node->iProtocol = aIpsecSpi.iProtocol; + spi_node->iInbound = aIpsecSpi.iInbound; +} + +TBool CIkev1SA::FindIpsecSPI(TUint32 aSPI, TBool aInbound) +{ + TIpsecSPI *spi_node; + for (TInt i = 0; i < iSPIList->Count(); i++) + { + spi_node = iSPIList->At(i); + if ( (spi_node->iSPI == aSPI) && (spi_node->iInbound == aInbound) ) + { + return ETrue; + } + } + + return EFalse; +} + +// +//Deletes a TIpsecSPI matching aSPI +// +TBool CIkev1SA::DeleteIpsecSPI(TUint32 aSPI, TBool aInbound) +{ + TIpsecSPI *spi_node; + for (TInt i = 0; i < iSPIList->Count(); i++) + { + spi_node = iSPIList->At(i); + if ( (spi_node->iSPI == aSPI) && (spi_node->iInbound == aInbound) ) + { + delete spi_node; + iSPIList->Delete(i); + return ETrue; + } + } + + return EFalse; +} + +// +// Flush all Ipsec SA:s bound to this IKE SA from SADB and send Delete +// payload for all inbound SAs +// +void CIkev1SA::DeleteIpsecSAs() +{ + TIpsecSPI* spi_node; + TInt c = iSPIList->Count(); + for (TInt i = 0; i < c; i++) + { + spi_node = iSPIList->At(i); + if ( spi_node->iInbound ) + { + //Only the inbound ones notified to avoid receiving packets using an expired SA + //The opposite if receiving a Delete + DEBUG_LOG1(_L("Sending ISAKMP Delete payload for IPSec SPI %x"), + (int)ByteOrder::Swap32(spi_node->iSPI)); + + // Call to delete may fail (delete sends DELETE payloads, and the data connection + // may not be open anymore). This is non-fatal, however. + TRAPD(err, iPluginSession.DeleteIpsecSAL(&iHdr, spi_node)); + if (err == KErrNone) + { + // DELETE sent successfully + DEBUG_LOG(_L("CIkev1SA::DeleteIpsecSAsL() IPsec SA delete OK")); + } + else if (err == KErrNotFound) + { + // Non-fatal leave occured (couldn't send DELETE due to invalid connection) + // We can still continue purging IPSEC SAs. + DEBUG_LOG(_L("CIkev1SA::DeleteIpsecSAsL() IPsec SA delete failed due non-existing connection. Non-fatal, continuing")); + } + else + { + // Fatal leave (e.g. out of memory etc) + DEBUG_LOG(_L("CIkev1SA::DeleteIpsecSAsL() IPsec SA deletion error. Fatal.")); + iPluginSession.HandleError(err); + return; + } + } + iPluginSession.DeleteIpsecSA(spi_node->iSPI, spi_node->iSrcAddr, spi_node->iDstAddr, spi_node->iProtocol); + delete spi_node; + } + iSPIList->Reset(); //Empties the full list at once +} + +// +// void CIkev1SA::DeleteIpsecSAsForced() +// +void CIkev1SA::DeleteIpsecSAsForced() +{ + TIpsecSPI* spi_node; + TInt c = iSPIList->Count(); + for (TInt i = 0; i < c; i++) + { + spi_node = iSPIList->At(i); + iPluginSession.DeleteIpsecSA( spi_node->iSPI, + spi_node->iSrcAddr, + spi_node->iDstAddr, + spi_node->iProtocol ); + delete spi_node; + } + iSPIList->Reset(); +} + +void CIkev1SA::EventHandlerL() +{ + // + // The implementation for class MDpdHeartBeatEventHandler virtual function + // This method is called by an CIkeKeepAlive object instance when + // DPD heartbeat timeout has elapsed. + // + if ( !iExpired && iSPIList->Count() ) + iPluginSession.KeepAliveIkeSAL(&iHdr); +} + +void CIkev1SA::CancelRekey() + { + if ( iLeftOverTime != 0 ) + { + DEBUG_LOG1(_L("CIkev1SA::CancelRekey, remaining time=%d"), iLeftOverTime ); + iRemainingTime = iLeftOverTime; + iLeftOverTime = 0; + } + } + +// +//class CIpsecSPIList : public CArrayPtr +// +CIpsecSPIList::CIpsecSPIList(TInt aGranularity) : CArrayPtrFlat(aGranularity){} +CIpsecSPIList::~CIpsecSPIList() {ResetAndDestroy();} +