diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev2lib/src/ikev2pluginsession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev2lib/src/ikev2pluginsession.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,1177 @@ +/* +* Copyright (c) 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: +* +*/ + +#include + +#include "ikev2pluginsession.h" +#include "ikev2plugin.h" +#include "ikev2Negotiation.h" +#include "ikepolparser.h" +#include "ikedebug.h" +#include "ikev2SA.h" +#include "ikev2SAdata.h" +#include "ikedatainterface.h" +#include "ipsecsadata.h" +#include "ikev2pfkey.h" +#include "ipsecsalist.h" +#include "ipsecpolicyutil.h" +#include "ikev2messagesendqueue.h" + + +CIkev2PluginSession* CIkev2PluginSession::NewL( TUint32 aVpnIapId, + TUint32 aVpnNetId, + TUint32 aVpnInterfaceIndex, + MIkeDataInterface& aDataInterface, + CIkev2PlugIn& aPlugin, + CPFKeySocketIf& aPfKeySocketIf, + CIpsecPolicyUtil& aIpsecPolicyUtil, + MKmdEventLoggerIf& aEventLogger, + MIkeDebug& aDebug ) + { + CIkev2PluginSession* self = new (ELeave) CIkev2PluginSession( aVpnIapId, aVpnNetId, + aVpnInterfaceIndex, aDataInterface, + aPlugin, aPfKeySocketIf, aIpsecPolicyUtil, + aEventLogger, aDebug ); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + + return self; + } + + +CIkev2PluginSession::CIkev2PluginSession( TUint32 aVpnIapId, + TUint32 aVpnNetId, + TUint32 aVpnInterfaceIndex, + MIkeDataInterface& aDataInterface, + CIkev2PlugIn& aPlugin, + CPFKeySocketIf& aPfKeySocketIf, + CIpsecPolicyUtil& aIpsecPolicyUtil, + MKmdEventLoggerIf& aEventLogger, + MIkeDebug& aDebug ) +: iVpnIapId(aVpnIapId), iVpnNetId(aVpnNetId), iDataInterface(aDataInterface), iPlugin(aPlugin), + iPfKeySocketIf(aPfKeySocketIf), iIpsecPolicyUtil(aIpsecPolicyUtil), iEventLogger(aEventLogger), + iDebug(aDebug), iVpnInterfaceIndex(aVpnInterfaceIndex) + { + } + + +void CIkev2PluginSession::ConstructL() + { + TPtr8 ptr((TUint8*)&iSAIdSeed, sizeof(iSAIdSeed)); + ptr.SetLength(sizeof(iSAIdSeed)); + TRandom::RandomL(ptr); + iSAIdSeed &= 0x7fffffff; // Reset the most significant bit + DEBUG_LOG1(_L("CIkev2Plugin::ConstructL, SAId seed: %d"), iSAIdSeed ); + } + + +CIkev2PluginSession::~CIkev2PluginSession() + { + //Makes sure that all the negotiations and + //Sa data structures are deleted: + while ( iFirstNegotiation ) + { + CIkev2Negotiation* negotiation = iFirstNegotiation; + iFirstNegotiation = iFirstNegotiation->iNext; + + delete negotiation; + } + + while(iFirstIkev2SA) + { + CIkev2SA* ikeV2Sa = iFirstIkev2SA; + iFirstIkev2SA = ikeV2Sa->iNext; + + delete ikeV2Sa; + } + + delete iMessageSendQue; + delete iReceiver; + delete iIkeData; + delete iDeactivationTimer; + + iPlugin.PluginSessionDeleted(this); + } + + +void CIkev2PluginSession::NegotiateWithHost( const CIkeData& aIkeData, + TVPNAddress& aInternalAddress, + TRequestStatus& aStatus ) + { + __ASSERT_DEBUG(iClientStatusNegotiate == NULL, + User::Invariant()); + + iClientStatusNegotiate = &aStatus; + *iClientStatusNegotiate = KRequestPending; + + iInternalAddress = &aInternalAddress; + + TRAPD(err, DoNegotiateWithHostL(aIkeData)); + if (err != KErrNone) + { + DoCompleteNegotiateWithHost(err); + } + } + + +void CIkev2PluginSession::DoNegotiateWithHostL( const CIkeData& aIkeData ) + { + iIkeData = CIkeData::NewL(&aIkeData); + + iReceiver = CIkev2Receiver::NewL( iDataInterface, + *this ); + + iMessageSendQue = CIkev2MessageSendQueue::NewL(iDataInterface, + iIkeData->iAddr, + iIkeData->iDscp, + iIkeData->iNatKeepAlive, + iDebug); + + + TInetAddr physicalAddr; + iDataInterface.GetLocalAddress(physicalAddr); + TInetAddr sgwAddr(iIkeData->iAddr); + + // Negotiation ownership is transferred to the plugin + // before leave can occur. + iSAIdSeed++; + + if (aIkeData.iUseInternalAddr) + { + CIkev2Negotiation* Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, iEventLogger, + *iMessageSendQue, iDebug, iIkeData, + iVpnIapId, iSAIdSeed, + physicalAddr, sgwAddr); + + Negotiation->StartIkeSANegotiationL(); + } + else + { + //If internall addressing is not in use, we do not do anything else + //in this phase. The actual IKE negotiation is trickered by an Acquire + //PFKEY message from the IPsec, when there is actual data between the SGW and + //the phone. + DoCompleteNegotiateWithHost( KErrNone); + } + } + + +void CIkev2PluginSession::CancelNegotiateWithHost() + { + + if (iClientStatusNegotiate != NULL) + { + //If the Negotiate with host is cancelled we pretty much do a silent close + //for the connection + + while ( iFirstNegotiation ) + { + CIkev2Negotiation* negotiation = iFirstNegotiation; + iFirstNegotiation = iFirstNegotiation->iNext; + + delete negotiation; + } + + while(iFirstIkev2SA) + { + CIkev2SA* ikeV2Sa = iFirstIkev2SA; + iFirstIkev2SA = ikeV2Sa->iNext; + + delete ikeV2Sa; + } + DoCompleteNegotiateWithHost(KErrCancel); + } + } + + +void CIkev2PluginSession::DeleteSession( const TBool aSilentClose, + TRequestStatus& aStatus ) + { + DEBUG_LOG1(_L("Deactivating IKE SA:s for vpn iap %d"), iVpnIapId); + + __ASSERT_DEBUG(iClientStatusDelete == NULL, User::Invariant()); + iClientStatusDelete = &aStatus; + *iClientStatusDelete = KRequestPending; + + TInt err = KErrNone; + TBool doSilentClose = aSilentClose; + //Deletes all ongoing ike negotiations + while ( iFirstNegotiation ) + { + CIkev2Negotiation* negotiation = iFirstNegotiation; + iFirstNegotiation = iFirstNegotiation->iNext; + + delete negotiation; + } + + TBool deactivating = EFalse; + while(iFirstIkev2SA) + { + CIkev2SA* ikeV2Sa = iFirstIkev2SA; + iFirstIkev2SA = ikeV2Sa->iNext; + + if (!doSilentClose) + { + TRAP(err, DoDeleteIkeSAExhangeL(ikeV2Sa->iIkeV2SaData)); + if (err == KErrNone) + { + deactivating = ETrue; + } + else + { + //If we can't start the IKE SA delete exhange, + //we do following expection handling: + //1. Possible already active delete exhanges can continue as they were. + //2. The IKE SA, which delete exchange failured, is deleted silently. + //3. The rest of the IKE SAs are deleted silently. + //4. The caller is notified with the error returned by the failed delete + // exchange attempt, if no delete exhanges are in progress. + //5. If there is ongoing delete exhange(s), the caller is notified with the + // status of last delete exhange, which completes. + DEBUG_LOG1(_L("CIkev2PluginSession::DeleteSession: Can't start IKE SA delete exhange (%d)"), + err ); + doSilentClose = ETrue; + } + } + delete ikeV2Sa; + } + + if (deactivating) + { + TRAP( err, iDeactivationTimer = CIkev2DeactivationTimer::NewL(*this) ); + } + + if (deactivating && + err == KErrNone) + { + iDeactivationTimer->IssueRequest(); + } + else + { + delete iIkeData; + iIkeData = NULL; + DoCompleteDeleteSession(err); + } + } + + +void CIkev2PluginSession::DoDeleteIkeSAExhangeL(TIkev2SAData& aIkev2SAdata) + { + DEBUG_LOG1(_L("Deleting IKE SA SAID = %d"), aIkev2SAdata.SaId()); + + __ASSERT_DEBUG(iFirstNegotiation == NULL, User::Invariant()); + + CIkev2Negotiation* negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, + iEventLogger, *iMessageSendQue, + iDebug, aIkev2SAdata); + CleanupStack::PushL(negotiation); + negotiation->StartIkeSADeleteL(); + CleanupStack::Pop(negotiation); + + __ASSERT_DEBUG( !negotiation->Stopped(), User::Invariant() ); + + } + + +void CIkev2PluginSession::CancelDeleteSession() + { + if (iClientStatusDelete != NULL) + { + //If the delete sessionis cancelled we pretty much do a silent close + //for the connection + iMessageSendQue->CancelAll(); + iReceiver->Cancel(); + delete iDeactivationTimer; + iDeactivationTimer = NULL; + + while ( iFirstNegotiation ) + { + CIkev2Negotiation* negotiation = iFirstNegotiation; + iFirstNegotiation = iFirstNegotiation->iNext; + + delete negotiation; + } + + while(iFirstIkev2SA) + { + CIkev2SA* ikeV2Sa = iFirstIkev2SA; + iFirstIkev2SA = ikeV2Sa->iNext; + + delete ikeV2Sa; + } + DoCompleteDeleteSession(KErrCancel); + } + } + + +void CIkev2PluginSession::NotifyError( TRequestStatus& aStatus ) + { + aStatus = KRequestPending; + iClientStatusNotifyError = &aStatus; + } + +void CIkev2PluginSession::CancelNotifyError() + { + if (iClientStatusNotifyError != NULL) + { + DoCompleteNotifyError(KErrCancel); + } + } + + +void CIkev2PluginSession::NotifyInternalAddressChanged( TVPNAddress& aInternalAddress, + TRequestStatus& aStatus ) + { + __ASSERT_DEBUG(iClientStatusInternalAddressChange == NULL, + User::Invariant()); + + __ASSERT_DEBUG(iChangedInternalAddress == NULL, + User::Invariant()); + + + iClientStatusInternalAddressChange = &aStatus; + *iClientStatusInternalAddressChange = KRequestPending; + + iChangedInternalAddress = &aInternalAddress; + } + + +void CIkev2PluginSession::CancelNotifyInternalAddressChanged() + { + if (iClientStatusInternalAddressChange != NULL) + { + __ASSERT_DEBUG(iChangedInternalAddress != NULL, User::Invariant()); + iChangedInternalAddress = NULL; + User::RequestComplete(iClientStatusInternalAddressChange, KErrCancel); + } + } + + +void CIkev2PluginSession::LinkNegotiation(CIkev2Negotiation* aNegotiation) +{ + ASSERT(aNegotiation); + aNegotiation->iNext = iFirstNegotiation; + iFirstNegotiation = aNegotiation; +} + + +void CIkev2PluginSession::RemoveNegotiation(CIkev2Negotiation* aNegotiation) + { + CIkev2Negotiation* Prev = NULL; + CIkev2Negotiation* Neg = iFirstNegotiation; + + while ( Neg ) + { + if ( Neg == aNegotiation ) + { + if ( Prev ) + Prev->iNext = Neg->iNext; + else iFirstNegotiation = Neg->iNext; + break; + } + Prev = Neg; + Neg = Neg->iNext; + } + } + +// +// Find an IKEv2 SA using SA Id as search argument +// +CIkev2SA* CIkev2PluginSession::FindIkev2SA(TUint32 aSAId, TInt aRequiredState, TInt aNewState) +{ + CIkev2SA* Sa = iFirstIkev2SA; + while ( Sa ) + { + if ( ( Sa->iIkeV2SaData.SaId() == aSAId ) + && + ( ( aRequiredState == KSaStateNotDefined) || + ( aRequiredState == Sa->iIkeV2SaData.iSAState ) ) ) + { + if ( aNewState != KSaStateNotDefined ) + Sa->iIkeV2SaData.iSAState = aNewState; + break; + } + Sa = Sa->iNext; + } + return Sa; +} + + +TBool CIkev2PluginSession::UpdateIkev2SAL(TIkev2SAData* aIkev2SAData, TIkeV2IpsecSAData* aIpsecSAData) + { + ASSERT(aIkev2SAData); + CIkev2SA* Ikev2SA = FindIkev2SA(aIkev2SAData->SaId(), KSaStateNotDefined, KSaStateNotDefined); + if ( Ikev2SA ) + { + Ikev2SA->UpdateL(aIkev2SAData, aIpsecSAData); + return ETrue; + } + else + { + return EFalse; + } + } + +TIkeV2IpsecSAData* CIkev2PluginSession::FindIpsecSAData(TUint32 aSAId, const TDesC8& aSpi, TBool aInbound) + { + __ASSERT_ALWAYS(aSpi.Length() == 4, User::Invariant()); + + _LIT8(KZeroSpi, ""); + TIkeV2IpsecSAData* SaData = NULL; + CIkev2SA* Ikev2SA = FindIkev2SA(aSAId, KSaStateNotDefined, KSaStateNotDefined); + if ( Ikev2SA ) + { + if ( aInbound ) + SaData = Ikev2SA->FindIpsecSaData(aSpi, KZeroSpi, EFalse); + else SaData = Ikev2SA->FindIpsecSaData(KZeroSpi, aSpi, EFalse); + } + return SaData; + } + + +// +// Delete an IKEv2 SA using SA Id as search argument +// +void CIkev2PluginSession::DeleteIkev2SA(TUint32 aSAId) +{ + CIkev2SA* Sa = iFirstIkev2SA; + CIkev2SA* PrevSa = NULL; + while ( Sa ) + { + if ( Sa->iIkeV2SaData.SaId() == aSAId ) + { + if ( PrevSa ) + { + PrevSa->iNext = Sa->iNext; + } + else + { + iFirstIkev2SA = Sa->iNext; + } + if (Sa->iIkeV2SaData.iFloatedPort) + { + iMessageSendQue->SaBehindNatDeleted(Sa->iIkeV2SaData.SaId()); + } + delete Sa; + break; + } + PrevSa = Sa; + Sa = Sa->iNext; + } +} + +TUint32 CIkev2PluginSession::GetSAId() + { + iSAIdSeed++; + return iSAIdSeed; + } + +TBool CIkev2PluginSession::CreateIkev2SAL(TIkev2SAData& aIkev2SAData) + { + CIkev2SA* Ikev2SA = CIkev2SA::NewL(*this, aIkev2SAData, iDebug); + if (aIkev2SAData.iFloatedPort) + { + CleanupStack::PushL(Ikev2SA); + iMessageSendQue->NewSaBehindNatL(aIkev2SAData.SaId()); + CleanupStack::Pop(Ikev2SA); + } + Ikev2SA->iNext = iFirstIkev2SA; + iFirstIkev2SA = Ikev2SA; + + return ETrue; + } + +void CIkev2PluginSession::IkeSaCompleted(TInt aStatus, TVPNAddress& aInternalAddress) +{ + if (iClientStatusNegotiate != NULL) + { + //This is the first IKE sa of this session + if (!aInternalAddress.iVPNIfAddr.IsUnspecified()) + { + *iInternalAddress = aInternalAddress; + } + + // Completion is postponed, if IPsec SAs have not yet been updated. + if (iActivated || + aStatus != KErrNone) + { + DoCompleteNegotiateWithHost(aStatus); + } + } + else if (aStatus == KErrNone) + { + //This is not the first IKE SA in this session. + //If IA has changed we notify the possible address change + if(!aInternalAddress.iVPNIfAddr.IsUnspecified()) + { + VirtualIpChanged(aInternalAddress); + } + } + else if(iClientStatusNotifyError != NULL) + { + //Ike sa establishmet has failed. + DoCompleteNotifyError(aStatus); + } +} + + +void CIkev2PluginSession::VirtualIpChanged(TVPNAddress& aVirtualIp) + { + if (iClientStatusInternalAddressChange != NULL) + { + __ASSERT_DEBUG(iChangedInternalAddress != NULL, User::Invariant()); + *iChangedInternalAddress = aVirtualIp; + User::RequestComplete(iClientStatusInternalAddressChange, KErrNone); + iChangedInternalAddress = NULL; + } + } + +void CIkev2PluginSession::StartResponding() + { + iCurrIkeSaRespCount++; + } + + +void CIkev2PluginSession::StopResponding() + { + if (iCurrIkeSaRespCount) + { + iCurrIkeSaRespCount--; + } + } + + +void CIkev2PluginSession::DeleteIpsecSAData(TUint32 aSAId, const TDesC8& aSpi, TBool aInbound) + { + __ASSERT_DEBUG(aSpi.Length() == 4, User::Invariant()); + _LIT8(KZeroSpi, ""); + CIkev2SA* Ikev2SA = FindIkev2SA(aSAId, KSaStateNotDefined, KSaStateNotDefined); + if ( Ikev2SA ) + { + if ( aInbound ) + Ikev2SA->DeleteIpsecSaData(aSpi, KZeroSpi); + else Ikev2SA->DeleteIpsecSaData(KZeroSpi, aSpi); + } + } + +void CIkev2PluginSession::IkeSaDeleted(TInt aStatus) + { + if (iClientStatusDelete != NULL) + { + DoCompleteDeleteSession(aStatus); + } + else if (aStatus != KErrNone && iClientStatusNotifyError != NULL) + { + DoCompleteNotifyError(aStatus); + } + else if (aStatus != KErrNone && iClientStatusNegotiate != NULL) + { + TVPNAddress dummyVirtualIp; + IkeSaCompleted(aStatus,dummyVirtualIp); + } + } + + +CIpsecSaSpecList* CIkev2PluginSession::GetIPsecSaSpecListL( const TInetAddr& aLocalAddr, const TInetAddr& aLocalMask, + const TInetAddr& aRemoteAddr, const TInetAddr& aRemoteMask, + TInt aProtocol ) + { + CIpsecSaSpecList* saSpecList = iIpsecPolicyUtil.GetIpseSaSpecListLC( aLocalAddr, aLocalMask, + aRemoteAddr, aRemoteMask, + aProtocol, iVpnNetId ); + CleanupStack::Pop(saSpecList); + + return saSpecList; + } + + +TBool CIkev2PluginSession::InheritIpsecSas(TUint32 aDstSAId, TUint32 aSrcSAId) + { + CIkev2SA* DstIkev2SA = FindIkev2SA(aDstSAId, KSaStateNotDefined, KSaStateNotDefined); + if ( DstIkev2SA ) + { + CIkev2SA* SrcIkev2SA = FindIkev2SA(aSrcSAId, KSaStateNotDefined, KSaStateNotDefined); + if ( SrcIkev2SA ) + { + DstIkev2SA->SetIpsecSaQue(SrcIkev2SA->GetIpsecSaQue()); + return ETrue; + } + } + return EFalse; + } + + +TUint32 CIkev2PluginSession::VpnInterfaceIndex() const + { + return iVpnInterfaceIndex; + } + +TBool CIkev2PluginSession::RemoteAddrChanged(TIkev2SAData* aIkev2SAData, TInetAddr& aNewIp) + { + __ASSERT_DEBUG(aIkev2SAData, User::Invariant()); + CIkev2SA* Ikev2SA = FindIkev2SA(aIkev2SAData->SaId(), KSaStateNotDefined, KSaStateNotDefined); + if ( Ikev2SA ) + return Ikev2SA->RemoteAddrChanged(aNewIp); + else return ETrue; + } + +void CIkev2PluginSession::KeepAliveIkeSAL(TIkev2SAData* aIkev2SAdata) + { + ASSERT(aIkev2SAdata); + CIkev2Negotiation* Negotiation = FindNegotiation(aIkev2SAdata->SaId(), KSaStateNotDefined); + if ( Negotiation ) + { + //There is already some negotiation going on this SA, don't send keep-alive + return; + } + + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, + iEventLogger, *iMessageSendQue, + iDebug, *aIkev2SAdata); + CleanupStack::PushL(Negotiation); + Negotiation->SendKeepAliveMsgL(); + if ( Negotiation->Stopped() ) + { + CleanupStack::PopAndDestroy(Negotiation); + } + else + { + CleanupStack::Pop(Negotiation); + } + } + +CIkev2Negotiation* CIkev2PluginSession::FindNegotiation(TUint32 aSAId, TInt aRequiredState) + { + // + // Find IKEv2 negotiation object using SAId as search argument + // + CIkev2Negotiation* Neg = iFirstNegotiation; + while ( Neg ) + { + if ( ( Neg->iHdr.SaId() == aSAId ) + && + ( ( aRequiredState == KSaStateNotDefined) || + ( aRequiredState == Neg->iHdr.iSAState ) ) ) + { + break; + } + + Neg = Neg->iNext; + } + return Neg; + } + +TBool CIkev2PluginSession::DeleteIkeSAL(TIkev2SAData* aIkev2SAdata, TBool aNormal) + { + ASSERT(aIkev2SAdata); + // + // An IKE SA delete request received + // Check first does there exists an ongoing negotiation on this IKE + // SA deleted and delete this block. + // Allocate a new negotiation with TIkev2SAData and initiate IKE SA + // deletion request + // + DEBUG_LOG1(_L("Deleting IKE SA SAID = %d"), aIkev2SAdata->SaId()); + + CIkev2Negotiation* Negotiation = FindNegotiation(aIkev2SAdata->SaId(), KSaStateNotDefined); + while ( Negotiation ) + { + delete Negotiation; // destructor removes object from queue, too + Negotiation = FindNegotiation(aIkev2SAdata->SaId(), KSaStateNotDefined); + } + + TBool Started = EFalse; + if ( aNormal ) + { + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, + iEventLogger, *iMessageSendQue, + iDebug, *aIkev2SAdata); + CleanupStack::PushL(Negotiation); + Negotiation->StartIkeSADeleteL(); + CleanupStack::Pop(Negotiation); + if ( Negotiation->Stopped() ) + delete Negotiation; + else Started = ETrue; + } + else + { + DEBUG_LOG(_L("Forced close, no delete payload(s) sent")); + } + + DeleteIkev2SA(aIkev2SAdata->SaId()); + + return Started; + } + +void CIkev2PluginSession::RekeyIkeSAL(TIkev2SAData* aIkev2SAdata) + { + ASSERT(aIkev2SAdata); + // + // Rekey specified IKE SA + // + DEBUG_LOG1(_L("Starting to rekey IKE SA SAID = %d"), aIkev2SAdata->SaId()); + CIkev2Negotiation* Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, + iEventLogger, *iMessageSendQue, + iDebug, *aIkev2SAdata); + CleanupStack::PushL(Negotiation); + Negotiation->BuildIkeSaRekeyMsgL(ETrue); + if ( Negotiation->Stopped() ) + CleanupStack::PopAndDestroy(Negotiation); + else CleanupStack::Pop(Negotiation); + } + +void CIkev2PluginSession::IkeMsgReceived( const ThdrISAKMP& aIkeMsg, + const TInetAddr& aSrcAddr, + TInt aLocalPort) + { + TRAPD(err, IkeMessageReceivedL(aIkeMsg, aSrcAddr, aLocalPort)); + if (err != KErrNone) + { + //Leave that we have not been able to handle + //above layers. We close the connection and report an error. + IkeSaDeleted(err); + } + } + +// --------------------------------------------------------------------------- +// From class MIkev2ReceiverCallback +// Handles notification about receive error. +// --------------------------------------------------------------------------- +// +void CIkev2PluginSession::ReceiveError( TInt aError ) + { + IkeSaDeleted( aError ); + } + +void CIkev2PluginSession::IkeMessageReceivedL(const ThdrISAKMP& aIkeMessage, + const TInetAddr &aRemote, + TUint16 aLocalPort) + { + + // + // Do sanity check Parse incoming IKE message + // + TUint32 NegotiationId; + if ( !CheckIkeMessageHeader(aIkeMessage, NegotiationId) ) + return; // Format error in received IKE message header + + TBool CleanUpUsed = EFalse; + CIkev2Negotiation* Negotiation; + if ( NegotiationId ) + { + // + // Try to find ongoing IKEv2 negotiation with Id + // + Negotiation = FindNegotiation(NegotiationId, KSaStateNotDefined); + if ( !Negotiation ) + { + if (!(aIkeMessage.GetFlags() & IKEV2_RESPONSE_MSG)) + { + // + // Try to find an IKEv2 SA with negotiation ID + // + TIkev2SAData* Ikev2SAdata = FindIkev2SAData(NegotiationId, + KSaStateNotDefined, KSaStateNotDefined); + if ( Ikev2SAdata ) + { + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, + iEventLogger, *iMessageSendQue, + iDebug, *Ikev2SAdata); + CleanupStack::PushL(Negotiation); + CleanUpUsed = ETrue; + } + else + { + DEBUG_LOG(_L("Receive IKE message cannot be associated")); + return; + } + } + else + { + DEBUG_LOG(_L("Received response message, but we don't have associated negotiation")); + DEBUG_LOG(_L("--> Message silently discarded.")); + return; + } + } + } + else + { + // + // Negotiation ID has zero value. This must be an IKE_SA_INIT + // message from peer where Responder SPI has zero value + // Get a new negotiation object + // + + TInetAddr localAddr; + iDataInterface.GetLocalAddress(localAddr); + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, iEventLogger, + *iMessageSendQue, iDebug, + iIkeData, iVpnIapId, this->GetSAId(), + localAddr, + aRemote); + CleanupStack::PushL(Negotiation); + if ( !Negotiation->StartRespondingL(aIkeMessage) ) + { + if ( Negotiation->Stopped() ) + CleanupStack::PopAndDestroy(Negotiation); + else CleanupStack::Pop(Negotiation); + return; + } + CleanUpUsed = ETrue; + } + + Negotiation->ProcessIkeMessageL(aIkeMessage, (TInetAddr&)aRemote, aLocalPort); + if ( CleanUpUsed ) + CleanupStack::Pop(Negotiation); + + if ( Negotiation->Stopped() ) + delete Negotiation; + } + +TBool CIkev2PluginSession::CheckIkeMessageHeader(const ThdrISAKMP& aIkeMessage, TUint32& NegotiationId) + { + // + // Do the following sanity checks to incoming IKE message fixed + // header + // -- Check that Exchange type has some value specified in IKEv2 + // -- Check that Next Payload has some value specified in IKEv2 + // -- Check that Inititor SPI has not "zero" value + // + TUint8 ExchangeType = aIkeMessage.GetExchange(); + if ( (ExchangeType < IKE_SA_INIT) || (ExchangeType > INFORMATIONAL) ) + { + DEBUG_LOG1(_L("Unsupported Exchange Type: %d"),ExchangeType); + return EFalse; + } + + TUint32 SPI_I_Low = aIkeMessage.GetSPI_I_Low32(); + TUint32 NegotiationId_I = aIkeMessage.GetNegotiationID_I(); + if ( (SPI_I_Low == 0 ) && ( NegotiationId_I == 0 ) ) + { + DEBUG_LOG(_L("Initiator SPI has zero value !\n")); + return EFalse; + } + // + // The negotiation id is a 32-bit (not zero) id value which + // unambiguously identiefies an IKEv2 negotiation object (CIkev2Negotiation). + // This negotiation id is packed into the SPI value ( 32 most + // significant bits of SPI) defined by the local end (=us). + // Get the negotiation id from local SPI in IKE message + // according to Initiator Bit in received IKE message header + // flags. + // Initiator = 1 ==> Get negotiation id from responder SPI + // Initiator = 0 ==> Get negotiation id from initiator SPI + // + aIkeMessage.GetFlags(); + if ( aIkeMessage.GetFlags() & IKEV2_INITIATOR ) + NegotiationId = aIkeMessage.GetNegotiationID_R(); + else NegotiationId = NegotiationId_I; + + return ETrue; + } + + +void CIkev2PluginSession::DeleteIpsecSA( const TUint32 aSPI, const TInetAddr& aSrc, + const TInetAddr& aDst, const TUint8 aProtocol ) + { + iPfKeySocketIf.DeleteSA(aSPI, aSrc, aDst, aProtocol); + } + + +void CIkev2PluginSession::AddSAL( const TIpsecSAData& aSAData ) + { + iPfKeySocketIf.AddSAL( aSAData ); + } + + +void CIkev2PluginSession::UpdateSAL( const TIpsecSAData& aSAData ) + { + iPfKeySocketIf.UpdateSAL( aSAData ); + } + + +TIkev2SAData* CIkev2PluginSession::FindIkev2SAData(TUint32 aSAId, TInt aRequiredState, TInt aNewState) + { + TIkev2SAData* SaData = NULL; + CIkev2SA* Ikev2SA = FindIkev2SA(aSAId, aRequiredState, aNewState); + if ( Ikev2SA ) + SaData = (TIkev2SAData*)&Ikev2SA->iIkeV2SaData; + return SaData; + } + +void CIkev2PluginSession::PfkeyMessageReceived(const TPfkeyMessage& aPfkeyMessage) + { + TRAPD(err, PfkeyMessageReceivedL(aPfkeyMessage)); + if (err != KErrNone) + { + //Leave that we have not been able to handle + //above layers. We close the connection and report an error. + IkeSaDeleted(err); + } + } + +void CIkev2PluginSession::PfkeyMessageReceivedL(const TPfkeyMessage& aPfkeyMessage) + { + // + // Process received PFKEY message according to message type + // + TIkev2SAData* Ikev2SAdata = NULL; + CIkev2Negotiation* Negotiation = NULL; + TBool CleanUpUsed = EFalse; + + __ASSERT_DEBUG(aPfkeyMessage.iBase.iMsg->sadb_msg_type != SADB_GETSPI, User::Invariant()); + switch ( aPfkeyMessage.iBase.iMsg->sadb_msg_type ) + { + case SADB_ADD: + { + if ( !iActivated ) + { + DEBUG_LOG(_L("Updating of IPsec SAs completed")); + iActivated = ETrue; + TVPNAddress dummyVirtualIp; + IkeSaCompleted(KErrNone,dummyVirtualIp); + } + break; + case SADB_ACQUIRE: + if ( iClientStatusDelete != NULL ) + { + DEBUG_LOG(_L("Acquire ignored because of ongoing deactivation.")); + return; + } + if (iFirstIkev2SA != NULL) + { + Ikev2SAdata = &(iFirstIkev2SA->iIkeV2SaData); + } + if ( Ikev2SAdata ) + { + DEBUG_LOG(_L("Found IKE SA for the acquire")); + // + // An IKE SA found for Acquire. Get a negotiation + // object for IKE Child SA exchange + // + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, + iEventLogger, *iMessageSendQue, + iDebug,*Ikev2SAdata); + CleanupStack::PushL(Negotiation); + CleanUpUsed = ETrue; + } + else + { + DEBUG_LOG(_L("No IKE SA for the Acquire. Creating new.")); + // + // No IKE SA found for Acquire not ongoing + // negotiation found for defined destination + // address. + // We shall start a new IKE SA negotiation to + // defined destination address. Find first the IKE + // policy for that destination address. + // + TInetAddr localAddr; + this->iDataInterface.GetLocalAddress(localAddr); + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, iEventLogger, + *iMessageSendQue, iDebug, iIkeData, + iVpnIapId, GetSAId(), + localAddr, + *(aPfkeyMessage.iDstAddr.iAddr)); + CleanupStack::PushL(Negotiation); + CleanUpUsed = ETrue; + } + Negotiation->ProcessAcquireL(aPfkeyMessage); + if ( CleanUpUsed ) + CleanupStack::Pop(Negotiation); + if ( Negotiation->Stopped() ) + delete Negotiation; + break; + + case SADB_EXPIRE: + if (aPfkeyMessage.iSoft.iExt) + { + // + // An IPSEC SA soft lifetime has expired. + // + // Try to find an existing IKE SA with remote address + // + if (iFirstIkev2SA != NULL) + { + Ikev2SAdata = &(iFirstIkev2SA->iIkeV2SaData); + } + if ( Ikev2SAdata ) + { + // + // An IKE SA found for soft expire. Get a negotiation + // object for IKE Child SA exchange + // + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, iEventLogger, + *iMessageSendQue, iDebug, *Ikev2SAdata); + CleanupStack::PushL(Negotiation); + DEBUG_LOG(_L("IKE SA found for soft expire IP.")); + + Negotiation->StartIpsecSaRekeyingL(aPfkeyMessage); + CleanupStack::Pop(Negotiation); + if ( Negotiation->Stopped() ) + delete Negotiation; + } + else + { + DEBUG_LOG(_L("No IKE SA found for soft expire IP")); + } + } + else + { + // + // An IPSEC SA has been expired. + // Try to find an existing IKE SA with remote address + // + if (iFirstIkev2SA != NULL) + { + Ikev2SAdata = &(iFirstIkev2SA->iIkeV2SaData); + } + if ( Ikev2SAdata ) + { + // + // An IKE SA found for Expire. Get a negotiation + // object for IKE Informational exchange + // + Negotiation = CIkev2Negotiation::NewL(*this, iPfKeySocketIf, iEventLogger, + *iMessageSendQue, iDebug, *Ikev2SAdata); + CleanupStack::PushL(Negotiation); + DEBUG_LOG(_L("IKE SA found for Expire IP")); + + Negotiation->ProcessExpireL(aPfkeyMessage); + CleanupStack::Pop(Negotiation); + if ( Negotiation->Stopped() ) + delete Negotiation; + } + else + { + DEBUG_LOG(_L("No IKE SA found Expire IP")); + } + } + break; + } + default: + break; + } + } + +TBool CIkev2PluginSession::MatchDestinationAddress( const TInetAddr& aDestAddr ) const + { + TBool match( EFalse ); + + if ( iIkeData ) + { + match = iIkeData->iAddr.Match( aDestAddr ); + } + return match; + } + +void CIkev2PluginSession::DeactivationTimeout() + { + IkeSaDeleted(KErrTimedOut); + } + +// --------------------------------------------------------------------------- +// Handles completion of client's negotiate request. +// --------------------------------------------------------------------------- +// +void CIkev2PluginSession::DoCompleteNegotiateWithHost( TInt aStatus ) + { + if ( aStatus != KErrNone ) + { + DoCancelActiveOperations(); + } + else + { + iActivated = ETrue; + } + + User::RequestComplete( iClientStatusNegotiate, aStatus ); + } + +// --------------------------------------------------------------------------- +// Handles completion of client's delete session request. +// --------------------------------------------------------------------------- +// +void CIkev2PluginSession::DoCompleteDeleteSession( TInt aStatus ) + { + delete iIkeData; + iIkeData = NULL; + delete iDeactivationTimer; + iDeactivationTimer = NULL; + + if ( aStatus != KErrCancel ) + { + DoCancelActiveOperations(); + } + User::RequestComplete( iClientStatusDelete, aStatus ); + } + +// --------------------------------------------------------------------------- +// Handles completion of client's notify error request. +// --------------------------------------------------------------------------- +// +void CIkev2PluginSession::DoCompleteNotifyError( TInt aStatus ) + { + if ( aStatus != KErrCancel ) + { + DoCancelActiveOperations(); + } + User::RequestComplete( iClientStatusNotifyError, aStatus ); + } + +// --------------------------------------------------------------------------- +// Cancels active operations. +// --------------------------------------------------------------------------- +// +void CIkev2PluginSession::DoCancelActiveOperations() + { + // Cancel active negotiation operations. + CIkev2Negotiation* negotiation = iFirstNegotiation; + while ( negotiation != NULL ) + { + negotiation->CancelOperation(); + negotiation = negotiation->iNext; + } + + // Cancel active IKE SA operations. + CIkev2SA* ikev2Sa = iFirstIkev2SA; + while( ikev2Sa != NULL ) + { + ikev2Sa->Cancel(); + ikev2Sa = ikev2Sa->iNext; + } + + DoCancelDataTransfer(); + } + +// --------------------------------------------------------------------------- +// Cancels data transfer. +// --------------------------------------------------------------------------- +// +void CIkev2PluginSession::DoCancelDataTransfer() + { + if ( iReceiver != NULL ) + { + iReceiver->StopReceive(); + } + if ( iMessageSendQue != NULL ) + { + iMessageSendQue->Cancel(); + iMessageSendQue->CancelAll(); + } + }