vpnengine/ikev2lib/src/ikev2pluginsession.cpp
changeset 0 33413c0669b9
child 10 68dc8923de26
--- /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 <random.h>
+
+#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();
+        }        
+    }