/*
* Copyright (c) 2008-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 plugin session
*
*/


#include <random.h>
#include <vpnlogmessages.rsg>
#include "ikev1plugin.h"
#include "ikedebug.h"
#include "ikev1negotiation.h"
#include "ikev1isakmpstream.h"
#include "pfkeymsg.h"
#include "ikepolparser.h"
#include "kmdapi.h"
#include "ikev1crack.h"
#include "ikev1infonegotiation.h"
#include "ikev1SA.h"
#include "ikev1sender.h"
#include "ikesocketdefs.h"
#include "ikedatainterface.h"
#include "vpnaddrinfo.h"
#include "ipsecsaspiretriever.h"

// CLASS HEADER
#include "ikev1pluginsession.h"

// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
CIkev1PluginSession* CIkev1PluginSession::NewL( TUint32 aVpnIapId,
                                                TUint32 aVpnNetId,
                                                TUint32 aVpnInterfaceIndex,
                                                MIkeDataInterface& aDataInterface,
                                                CIkev1Plugin& aPlugin,
                                                CPFKeySocketIf& aPFKeySocketIf,
                                                MIkeDebug& aDebug )
    {
    CIkev1PluginSession* self = new ( ELeave ) CIkev1PluginSession( aVpnIapId,
                                                                    aVpnNetId,
                                                                    aVpnInterfaceIndex,
                                                                    aDataInterface,
                                                                    aPlugin,
                                                                    aPFKeySocketIf,
                                                                    aDebug );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }
    
// ---------------------------------------------------------------------------
// Destructor.
// ---------------------------------------------------------------------------
//
CIkev1PluginSession::~CIkev1PluginSession()
    {
    // Cancel client's requests.
    DoCompleteNegotiateWithHost( KErrCancel );
    DoCompleteDeleteSession( KErrCancel );
    DoCompleteNotifyError( KErrCancel );
    DoCompleteInternalAddressChanged( KErrCancel );    
    
    if ( iDialogWaitQueue )
        {
        CIkev1Dialog::PurgeDialogQueue( iDialogWaitQueue );
        }   

    CIkev1Negotiation* negotiation;    
    while ( iFirstNegotiation )
        {
        negotiation = iFirstNegotiation;
        delete negotiation; // destructor removes object from queue, too
        }
    
    DoEmptySendQueue();
    iSendQueue.Close();
    
    while ( iIkev1SAs.Count() )
        {
        CIkev1SA* ikev1SA = iIkev1SAs[0];
        iIkev1SAs.Remove(0);
        delete ikev1SA;
        }
    iIkev1SAs.Close();
    
    delete iReceiver;
    delete iSender;
    delete iIkeData;
    delete iInternalAddress;
    
    iPlugin.IkePluginSessionDeleted( this );
    }

// ---------------------------------------------------------------------------
// Constructor.
// ---------------------------------------------------------------------------
//
CIkev1PluginSession::CIkev1PluginSession( TUint32 aVpnIapId,
                                          TUint32 aVpnNetId,
                                          TUint32 aVpnInterfaceIndex,
                                          MIkeDataInterface& aDataInterface,
                                          CIkev1Plugin& aPlugin,
                                          CPFKeySocketIf& aPFKeySocketIf,
                                          MIkeDebug& aDebug )
: iVpnIapId( aVpnIapId ),
  iVpnNetId( aVpnNetId ),
  iVpnInterfaceIndex( aVpnInterfaceIndex ),
  iDataInterface( aDataInterface ),
  iPlugin( aPlugin ),
  iPFKeySocketIf( aPFKeySocketIf ),
  iDebug( aDebug )
    {
    }

// ---------------------------------------------------------------------------
// Second phase construction.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::ConstructL()
    {
    TPtr8 ptr( (TUint8*)&iSAIdSeed, sizeof(iSAIdSeed) );
    ptr.SetLength( sizeof(iSAIdSeed) );
    TRandom::RandomL( ptr );  
    iSAIdSeed &= 0x7fffffff;  // Reset the most significant bit    

    iReceiver = CIkev1Receiver::NewL( iDataInterface,
                                      *this );   
    iSender = CIkev1Sender::NewL( iDataInterface,
                                  *this,
                                  iDebug );    
    
    DEBUG_LOG1( _L("CIkev1PluginSession::ConstructL, SAId seed: %d"),
            iSAIdSeed );
    }    

// ---------------------------------------------------------------------------
// Handles IKE SA deletion request.
// ---------------------------------------------------------------------------
//
TBool CIkev1PluginSession::DeleteIkeSA( TIkev1SAData* aIkev1SaData,
                                        TBool aSilentClose )
    {
    //
    // 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 TIkev1SAData and initiate IKE SA
    // deletion request
    //
    DEBUG_LOG1( _L("Deleting IKEv1 SA SAID =  %d"),
            aIkev1SaData->iSAId );
    
    CIkev1Negotiation* negotiation = FindNegotiation( aIkev1SaData->iSAId );
    while ( negotiation )
        {
        delete negotiation; // destructor removes object from queue, too
        negotiation = FindNegotiation( aIkev1SaData->iSAId );         
        }

    TBool started( EFalse );

    if ( !aSilentClose )
        {
        DeleteIpsecSAs( aIkev1SaData->iSAId );
        
        TRAPD( err, 
                {
                // Trap the SendDeleteL -- it can fail, but the failure won't be fatal 
                // (delete payload just won't be sent)
                negotiation = CIkev1Negotiation::NewL( this,
                                                       iPFKeySocketIf,
                                                       iDebug,
                                                       aIkev1SaData,
                                                       RESPONDER ); // Nevermind INITIATOR or RESPONDER
                negotiation->SendDeleteL( PROTO_ISAKMP );
                } );
                
        delete negotiation;
        negotiation = NULL;
        
        if ( err == KErrNone ) 
            {
            // DELETE payload sent succesfully.
            DEBUG_LOG( _L("CIkev1PluginSession::DeleteIkeSAL() IKEv1 delete send OK") );            
            started = ETrue;
            }
        else 
            {
            DEBUG_LOG1( _L("CIkev1PluginSession::DeleteIkeSAL() IKEv1 delete send failed, err=%d"), err );
            }
        }
#ifdef _DEBUG
    else
        {
        DEBUG_LOG( _L("Forced close, no delete payload(s) sent"));
        }
#endif    
    ExpireIkev1SA( aIkev1SaData->iSAId );  // Set expired to delete IKE SA
    
    if ( FindIkev1SA() == NULL &&
         FirstNegotiation() == NULL )
        {
        // Set error status, when expired IKE SA was the only IKE SA and there
        // is no ongoing negotiation.        
        iErrorStatus = KKmdIkeNegotFailed;
        }

    return started;
    }

// ---------------------------------------------------------------------------
// Handles IKE SA rekeying request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::RekeyIkeSAL( TIkev1SAData* aIkev1SaData,
                                       CSARekeyInfo* aSaRekeyInfo )
    {
    CIkev1Negotiation* negotiation = CIkev1Negotiation::NewL( this,
                                                              iPFKeySocketIf,
                                                              iDebug,
                                                              aIkev1SaData->iRemoteAddr,
                                                              EFalse );
    CleanupStack::PushL( negotiation );
    negotiation->SetRekeyInfo( aSaRekeyInfo );                
    negotiation->InitNegotiationL();
    if ( negotiation->Finished() )
        {
        CleanupStack::PopAndDestroy( negotiation );
        }
    else 
        {
        CleanupStack::Pop( negotiation );  
        }
    }

// ---------------------------------------------------------------------------
// Handles IKE SA keepalive request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::KeepAliveIkeSAL( TIkev1SAData* aIkev1SaData )
    {
    CIkev1Negotiation* negotiation = CIkev1Negotiation::NewL( this,
                                                              iPFKeySocketIf,
                                                              iDebug,
                                                              aIkev1SaData,
                                                              RESPONDER );
    CleanupStack::PushL( negotiation );
    negotiation->SendKeepAliveMsgL( aIkev1SaData );
    if ( negotiation->Finished() )
        {
        CleanupStack::PopAndDestroy( negotiation );
        }
    else
        {
        CleanupStack::Pop( negotiation );  
        }
    }

// ---------------------------------------------------------------------------
// Creates IKE SA.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::CreateIkev1SAL( TIkev1SAData& aIkev1SaData,
                                          CSARekeyInfo* aSaRekey )
    {
    CIkev1SA* ikev1SA = CIkev1SA::NewL( *this,
                                        aIkev1SaData,
                                        aSaRekey,
                                        iDebug );
    
    if ( !aIkev1SaData.iInitiator )
        {
        // Move SPI list from previous IKE SA to new IKE SA
        for ( TInt i=0;i<iIkev1SAs.Count();i++ )
            {
            CIkev1SA* previousSA = iIkev1SAs[i];
            if ( previousSA->iSPIList != NULL )
                {
                DEBUG_LOG(_L("Move SPI list to new IKE SA"));
                delete ikev1SA->iSPIList;
                ikev1SA->iSPIList = previousSA->iSPIList;
                previousSA->iSPIList = new (ELeave) CIpsecSPIList(1);  // Dummy;
                break;
                }
            }        
        }        
    
    // Cancel IKE SA rekeying from other IKE SAs.
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* cancelSA = iIkev1SAs[i];
        cancelSA->CancelRekey();
        }
    
    CleanupStack::PushL( ikev1SA );
    iIkev1SAs.AppendL( ikev1SA );
    CleanupStack::Pop( ikev1SA );    
    }

// ---------------------------------------------------------------------------
// Updates IKE SA.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::UpdateIkev1SAL( TUint32 aSaId,
                                          TBool aExpired,
                                          TIkev1SAData* aIkev1SaData )
    {
    if ( !aExpired )
        {
        CIkev1SA* Ikev1SA = FindIkev1SAWithId( aSaId );
        if ( Ikev1SA )
            {
            Ikev1SA->UpdateSAL( aExpired, aIkev1SaData );
            }
        }
    else
        {
        ExpireIkev1SA( aSaId );
        }
    }

// ---------------------------------------------------------------------------
// Expires IKE SA.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::ExpireIkev1SA( TUint32 aSaId )
    {
    CIkev1SA* ikev1SA = FindIkev1SAWithId( aSaId );
    if ( ikev1SA )
        {
        ikev1SA->ExpireSA();
        }
    }

// ---------------------------------------------------------------------------
// Removes IKE SA.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::RemoveIkeSA( CIkev1SA* aIkev1Sa,
                                       TInt aStatus )
    {        
    TInt dpdRetryCount( 0 );
    
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if ( aIkev1Sa == sa )
            {
            // Remove IKE SA from array.
            dpdRetryCount = sa->iHdr.iDPDRetry;
            iIkev1SAs.Remove(i);
            delete sa;
            break;
            }   
        }
    
    if ( (iErrorStatus == KErrNone) &&
         (dpdRetryCount > KMaxDpdRetryCount) &&
         (FindIkev1SA() == NULL) )
        {
        // If DPD retry count was reached for only IKE SA, set error status.
        iErrorStatus = KKmdIkeNoResponseErr;        
        }
        
    // If session deletion has been requested, complete session deletion
    // request.        
    DoCompleteDeleteSession( aStatus );
        
    // If fatal error has occured, complete error notification.    
    if ( iErrorStatus != KErrNone )
        {
        DoHandleError( iErrorStatus );
        }
    }

// IKE SA find methods.

CIkev1SA* CIkev1PluginSession::FindIkev1SA()
    {
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if ( (!sa->IsExpired()) )
            {
            return sa;
            }   
        }
    return NULL;
    }

CIkev1SA* CIkev1PluginSession::FindIkev1SA( const TCookie& aCookie_I,
                                            const TCookie& aCookie_R )
    {
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if ( (sa->iHdr.iCookie_I ==  aCookie_I) &&
             (sa->iHdr.iCookie_R ==  aCookie_R) &&
             (!sa->IsExpired()) )
            {
            return sa;
            }   
        }
    return NULL;
    }

CIkev1SA* CIkev1PluginSession::FindIkev1SAWithId( TUint32 aSaId )
    {
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if ( (sa->iHdr.iSAId == aSaId) &&
             (!sa->IsExpired()) )
            {
            return sa;
            }   
        }
    return NULL;
    }

CIkev1SA* CIkev1PluginSession::FindIkev1SA( const TInetAddr& aAddr )
    {
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if (  sa->iHdr.iRemoteAddr.Match(aAddr) &&
              (!sa->IsExpired()) ) 
            {
            return sa;
            }   
        }
    return NULL;
    }

CIkev1SA* CIkev1PluginSession::FindIkev1SA( const TInetAddr& aAddr,
                                            TUint32 aInboundSpi )
    {
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if (  sa->iHdr.iRemoteAddr.Match(aAddr) &&
              (!sa->IsExpired()) ) 
            {
            if ( sa->FindIpsecSPI(aInboundSpi, ETrue) )
                {
                return sa;
                }
            }   
        }
    return NULL;
    }

TIkev1SAData* CIkev1PluginSession::FindIkev1SAData()
    {
    TIkev1SAData* saData = NULL;        
    CIkev1SA* ikev1SA = FindIkev1SA();
    if ( ikev1SA )
        {
        saData = (TIkev1SAData*)&ikev1SA->iHdr;
        }
    return saData;
    }

TIkev1SAData* CIkev1PluginSession::FindIkev1SAData( const TCookie& aCookie_I,
                                                    const TCookie& aCookie_R )
    {
    TIkev1SAData* saData = NULL;        
    CIkev1SA* ikev1SA = FindIkev1SA( aCookie_I, aCookie_R );
    if ( ikev1SA )
        {
        saData = (TIkev1SAData*)&ikev1SA->iHdr;
        }
    return saData;
    }

TIkev1SAData* CIkev1PluginSession::FindIkev1SAData( TUint32 aSaId )
    {
    TIkev1SAData* saData = NULL;        
    CIkev1SA* ikev1SA = FindIkev1SAWithId( aSaId );
    if ( ikev1SA )
        {
        saData = (TIkev1SAData*)&ikev1SA->iHdr;
        }
    return saData;
    }

TIkev1SAData* CIkev1PluginSession::FindIkev1SAData( const TInetAddr& aAddr,
                                                    TUint32 aInboundSpi )
    {
    TIkev1SAData* saData = NULL;        
    CIkev1SA* ikev1SA = FindIkev1SA( aAddr, aInboundSpi );
    if ( ikev1SA )
        {
        saData = (TIkev1SAData*)&ikev1SA->iHdr;
        }
    return saData;
    }    

TIkev1SAData* CIkev1PluginSession::FindIkev1SADataWithAddr( const TInetAddr& aAddr )
    {
    TIkev1SAData* saData = NULL;        
    CIkev1SA* ikev1SA = FindIkev1SA( aAddr );
    if ( ikev1SA )
        {
        saData = (TIkev1SAData*)&ikev1SA->iHdr;
        }
    return saData;
    }    

// ---------------------------------------------------------------------------
// Handles IPsec SA deletion request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DeleteIpsecSAL( TIkev1SAData* aIkev1SaData,
                                          TIpsecSPI* aIpsecSpi )
    {
    //
    // Send a delete payload for specified IPSec SA 
    //
    CIkev1Negotiation* negotiation = CIkev1Negotiation::NewL( this,
                                                              iPFKeySocketIf,
                                                              iDebug,                                                              
                                                              aIkev1SaData,
                                                              RESPONDER );
    CleanupStack::PushL( negotiation );
    negotiation->SendDeleteL( aIpsecSpi->iProtocol,
                              aIpsecSpi->iSPI );
    CleanupStack::PopAndDestroy( negotiation );
    }

// ---------------------------------------------------------------------------
// Deletes IPsec SAs.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DeleteIpsecSAs( TUint32 aSaId )
    {
    CIkev1SA* ikev1SA = FindIkev1SAWithId( aSaId );
    if ( ikev1SA )
        {
        ikev1SA->DeleteIpsecSAs(); 
        }   
    }

// ---------------------------------------------------------------------------
// Deletes IPsec SPI.
// ---------------------------------------------------------------------------
//
TBool CIkev1PluginSession::DeleteIpsecSpi( TUint32 aSaId,
                                           TUint32 aSpi,
                                           TBool aInbound )
    {
    TBool status = EFalse;
    CIkev1SA* ikev1SA = FindIkev1SAWithId( aSaId );
    if ( ikev1SA )
        {
        status = ikev1SA->DeleteIpsecSPI( aSpi, aInbound );
        }
    return status;
    }

// ---------------------------------------------------------------------------
// Adds IPsec SPI to IKE SA.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::AddIpsecSPIToSAL( TUint32 aSaId,
                                            TIpsecSPI& aIpsecSpi )
    {
    CIkev1SA* ikev1SA = FindIkev1SAWithId( aSaId );
    if ( ikev1SA )
        {
        ikev1SA->AddIpsecSPIL( aIpsecSpi );
        }
    }

// ---------------------------------------------------------------------------
// Returns dialog anchor.
// ---------------------------------------------------------------------------
//
CIkev1Dialog** CIkev1PluginSession::DialogAnchor()
    {
    return &iDialogWaitQueue;
    }

// ---------------------------------------------------------------------------
// Returns debug trace interface.
// ---------------------------------------------------------------------------
//
MIkeDebug& CIkev1PluginSession::Debug()
    {
    return iDebug;
    }

// ---------------------------------------------------------------------------
// Gets SA id.
// ---------------------------------------------------------------------------
//
TUint32 CIkev1PluginSession::GetSAId()
    {
    iSAIdSeed++;
    return iSAIdSeed;
    }

// ---------------------------------------------------------------------------
// Deletes ISAKMP SAs.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DeleteISAKMPSAsL( TDeleteISAKMP* aDeletePayload,
                                            const CIkev1Negotiation& aInfoNegotiation )
    {
    TCookie cookie_I, cookie_R;

    // It should always be only one.
    for ( TInt i=0; i < aDeletePayload->NumSPI(); i++ )
        {
        if ( aDeletePayload->SPISize() < 2 * ISAKMP_COOKIE_SIZE ) //The ISAKMPSA SPI is the union of both cookies
            {
            DEBUG_LOG( _L("Bad SPI Size for a ISAKMP SA. (SA Not deleted)") );
            return;
            }
        cookie_I.Copy( aDeletePayload->SPI(i), ISAKMP_COOKIE_SIZE );
        cookie_R.Copy( aDeletePayload->SPI(i) + ISAKMP_COOKIE_SIZE,
                       ISAKMP_COOKIE_SIZE);

        CIkev1Negotiation* neg = iFirstNegotiation;
        while ( neg )
            {
            CIkev1Negotiation* current = neg;
            neg = neg->iNext;

            // Delete any active negotiations with the same cookies.
            // Currently used negotiation is not deleted.
            if ( ( current != &aInfoNegotiation ) &&
                 ( current->iCookie_I == cookie_I ) &&
                 ( current->iCookie_R == cookie_R ) )
                {
                DEBUG_LOG( _L("Active negotiation deleted.") );
                delete current;
                current = NULL;
                }
            }
        
        // Expire IKE SA.
        TIkev1SAData* sa = FindIkev1SAData( cookie_I, cookie_R );
        if ( sa )
            {
            UpdateIkev1SAL( sa->iSAId, ETrue );
            }
        }
    }

// ---------------------------------------------------------------------------
// Gets local IP address.
// ---------------------------------------------------------------------------
//
TInt CIkev1PluginSession::GetLocalAddress( TInetAddr& aAddr )
    {
    TInt err( KErrNone );
    if ( iLocalAddr.IsUnspecified() )
        {
        err = iDataInterface.GetLocalAddress( iLocalAddr );
        }
    
    aAddr = iLocalAddr;
    return err;
    }

// ---------------------------------------------------------------------------
// Sends IKE message.
// ---------------------------------------------------------------------------
//    
void CIkev1PluginSession::SendIkeMsgL( const TDesC8& aIkeMsg,
                                       TInetAddr& aDestAddr,
                                       TBool aUseNatPort )
    {
    // Construct buffer for storing IKE message data.
    TInt localPort = ( aUseNatPort ?
                       IkeSocket::KIkePort4500 :
                       IkeSocket::KIkePort500 );    
    TInt length = aIkeMsg.Length();    
    if ( localPort == IkeSocket::KIkePort4500 )
        {
        // Reserve space for <non-ESP marker>.
        length += NON_ESP_MARKER_SIZE;
        }    
    HBufC8* ikeMsg = HBufC8::NewL( length );    
    TPtr8 ptr = ikeMsg->Des();    
    if ( localPort == IkeSocket::KIkePort4500 )
        {    
        // Append <non-ESP marker> to the beginning of IKE message.
        TUint32 nonEspMarker = NON_ESP_MARKER;        
        TUint8* nonEspPtr = (TUint8*)&nonEspMarker;
        ptr.Append( nonEspPtr, NON_ESP_MARKER_SIZE );
        } 
    // Append IKE message data to descriptor.
    ptr.Append( aIkeMsg );    
    
    DoSendUdpDataL( ikeMsg, // Ownership transferred.
                    aDestAddr,
                    localPort,
                    0 );
    }

// ---------------------------------------------------------------------------
// Sends NAT keep-alive packet.
// ---------------------------------------------------------------------------
//    
void CIkev1PluginSession::SendNatKeepAliveL( TInetAddr& aDestAddr,
                                             const TDesC8& aData,
                                             TUint8 aDscp )
    {
    DEBUG_LOG(_L("CIkev1PluginSession::SendNatKeepAliveL"));
    HBufC8* udpData = HBufC8::NewL( aData.Length() );
    *udpData = aData;
    TInt localPort = aDestAddr.Port();
    DoSendUdpDataL( udpData, // Ownership transferred.
                    aDestAddr,
                    localPort,
                    aDscp );
    }

// ---------------------------------------------------------------------------
// Sends Nokia NAT keep-alive packet.
// ---------------------------------------------------------------------------
//    
void CIkev1PluginSession::SendNokiaNatKeepAliveL( TInetAddr& aDestAddr,
                                                  const TDesC8& aData,
                                                  TUint8 aDscp )    
    {
    DEBUG_LOG(_L("CIkev1PluginSession::SendNokiaNatKeepAliveL"));
    SendNatKeepAliveL( aDestAddr,
                       aData,
                       aDscp );
    }

// ---------------------------------------------------------------------------
// Handles completion of IKE SA establishment.
// ---------------------------------------------------------------------------
//    
void CIkev1PluginSession::IkeSaCompleted( TInt aStatus,
                                          CInternalAddress* aInternalAddress )
    {
    delete iInternalAddress;
    iInternalAddress = aInternalAddress;
    
    DoCompleteNegotiateWithHost( aStatus );
    }

// ---------------------------------------------------------------------------
// Deletes negotiation object.
// ---------------------------------------------------------------------------
//    
void CIkev1PluginSession::DeleteNegotiation( CIkev1Negotiation* aNegotiation )
    {
    TInt err = ErrorStatus();
    delete aNegotiation;        
    
    if ( err == KErrNone &&
         (iClientStatusNegotiate != NULL ||
          iClientStatusDelete != NULL) &&
         (FindIkev1SA() == NULL) )
        {
        // If negotiate or delete session request has been issued, and
        // there are no IKE SAs, client is completed with error.
        err = KKmdIkeNegotFailed;
        }

    DEBUG_LOG1(_L("IKEv1 negotiation deleted, status=%d"),
            err );
    
    if ( err != KErrNone )
        {
        //
        // IKE negotiation failed.
        //
        DoHandleError( err );
        }        
    }

// Negotiation linking and find methods.

void CIkev1PluginSession::LinkNegotiation( CIkev1Negotiation* aNegotiation )
    {  
    aNegotiation->iNext = iFirstNegotiation;  
    iFirstNegotiation = aNegotiation; 
    }    
    
CIkev1Negotiation* CIkev1PluginSession::FirstNegotiation()
    {
    return iFirstNegotiation;
    }

CIkev1Negotiation* CIkev1PluginSession::FindNegotiation( TCookie aInit,
                                                         TCookie aResp,
                                                         TUint8 aExchange,
                                                         TUint32 aMsgId )
    {
    CIkev1Negotiation* negotiation;
    TCookie NULL_COOKIE;
    NULL_COOKIE.FillZ(ISAKMP_COOKIE_SIZE);

    if ( aExchange == ISAKMP_EXCHANGE_INFO )
        {
        for ( negotiation = iFirstNegotiation;
              negotiation;
              negotiation = negotiation->iNext )
            {
            if ( (negotiation->iCookie_I.Compare(aInit) == 0 ) &&
                 ((negotiation->iCookie_R.Compare(aResp) == 0 ) ||
                   (negotiation->iCookie_R.Compare(NULL_COOKIE) == 0)) )
                {   
                return negotiation;
                }   
            }
        
        }   
    else
        {
        for ( negotiation = iFirstNegotiation;
              negotiation;
              negotiation = negotiation->iNext )
            {            
            if ( negotiation->iCookie_I.Compare(aInit) == 0 )
                {
                if ( (negotiation->iMessageId == aMsgId) ||
                     (negotiation->iMessageId == 0) )
                    {
                    if ( (negotiation->iCookie_R.Compare(aResp) == 0 ) ||
                         (negotiation->iCookie_R.Compare(NULL_COOKIE) == 0) ||
                         (aResp.Compare(NULL_COOKIE) == 0) )
                        {
                        return negotiation;
                        }
                    }
                }
            }           
        }   

    return NULL; // Not found
    }

CIkev1Negotiation* CIkev1PluginSession::FindNegotiation( TUint32 aSaId )
    {
    //
    // Find IKEv1 negotiation object using SA id as search argument 
    //
    CIkev1Negotiation* negotiation = iFirstNegotiation;
    while ( negotiation )
        {
        if ( negotiation->SAId() == aSaId )
            {   
            break;
            }   

        negotiation = negotiation->iNext;
        }   
    return negotiation;     
    }

void CIkev1PluginSession::RemoveNegotiation( CIkev1Negotiation* aNegotiation )
    {
    CIkev1Negotiation* prev = NULL;
    CIkev1Negotiation* negotiation  = iFirstNegotiation;
    
    while ( negotiation )
        {
        if ( negotiation == aNegotiation )
            {
            if ( prev )
                {
                prev->iNext = negotiation->iNext;
               }
           else 
               {
               iFirstNegotiation = negotiation->iNext;
               }
            break;  
            }
        prev = negotiation;
        negotiation = negotiation->iNext;
        }   
    }

// ---------------------------------------------------------------------------
// Handles completion of authentication dialog processing.
// ---------------------------------------------------------------------------
//    
TInt CIkev1PluginSession::AuthDialogCompletedL( CAuthDialogInfo* aUserInfo )
    {
    CIkev1Negotiation* negotiation = FindNegotiation( aUserInfo->SAId() );
    if ( negotiation )
        {
        DEBUG_LOG1( _L("Dialog completed for SAID: %d"),
                aUserInfo->SAId() );
        
        negotiation->AuthDialogCompletedL(aUserInfo);
        if ( negotiation->Finished() )
            {
            DeleteNegotiation( negotiation );
            }   
        return KErrNone;
        }   
    DEBUG_LOG1( _L("Dialog completed, no negotiation found for SAID: %d"),
            aUserInfo->SAId() );
    return KErrNotFound;
    }


// ---------------------------------------------------------------------------
// Handles change of internal address.
// ---------------------------------------------------------------------------
//    
TBool CIkev1PluginSession::InternalAddressChangedL( const CInternalAddress& aInternalAddr )
    {
    TBool internalAddressChanged( ETrue );
    
    if ( iInternalAddress )
        {
        if ( iInternalAddress->iClientIntAddr.Match(aInternalAddr.iClientIntAddr) )                    
            {
            internalAddressChanged = EFalse;
            }
        }        
    
    delete iInternalAddress;
    iInternalAddress = NULL;
    iInternalAddress = CInternalAddress::NewL( aInternalAddr );
    
    if ( internalAddressChanged )
        {
        DoCompleteInternalAddressChanged( KErrNone );
        }
    
    return internalAddressChanged;
    }

// ---------------------------------------------------------------------------
// Gets acceptable IPsec policies for specified selectors.
// ---------------------------------------------------------------------------
//
CIpsecSaSpecList* CIkev1PluginSession::GetIpseSaSpecListLC( const TInetAddr& aLocalAddr, const TInetAddr& aLocalMask, 
                                                            const TInetAddr& aRemoteAddr, const TInetAddr& aRemoteMask,
                                                            TInt aProtocol )

    {
    return iPlugin.GetIpseSaSpecListLC( aLocalAddr, aLocalMask, 
                                        aRemoteAddr, aRemoteMask,
                                        aProtocol, iVpnNetId );
    }

// ---------------------------------------------------------------------------
// Matches destination address to remote address in IKE policy data.
// ---------------------------------------------------------------------------
//
TBool CIkev1PluginSession::MatchDestinationAddress( const TInetAddr& aDestAddr )
    {
    TBool match( EFalse );
    
    if ( iIkeData )
        {
        match = iIkeData->iAddr.Match( aDestAddr );
        }    
    return match;
    }

// ---------------------------------------------------------------------------
// Handles fatal error.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::HandleError( TInt aStatus )
    {
    DoHandleError( aStatus );
    }

// ---------------------------------------------------------------------------
// Returns error status.
// ---------------------------------------------------------------------------
//
TInt CIkev1PluginSession::ErrorStatus()
    {
    return iErrorStatus;
    }

// ---------------------------------------------------------------------------
// Sets error status.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::SetErrorStatus( TInt aStatus )
    {
    if ( iErrorStatus == KErrNone )
        {
        iErrorStatus = aStatus;
        }
    }

// ---------------------------------------------------------------------------
// Returns VPN IAP id.
// ---------------------------------------------------------------------------
//
TUint32 CIkev1PluginSession::VpnIapId()
    {
    return iVpnIapId;
    }

// ---------------------------------------------------------------------------
// Returns VPN interface index.
// ---------------------------------------------------------------------------
//
TUint32 CIkev1PluginSession::VpnInterfaceIndex()
    {
    return iVpnInterfaceIndex;
    }

// ---------------------------------------------------------------------------
// Returns IKE policy data.
// ---------------------------------------------------------------------------
//
CIkeData& CIkev1PluginSession::IkeData()
    {
    __ASSERT_DEBUG( iIkeData != NULL,
                    User::Invariant() );
    return *iIkeData;
    }

// ---------------------------------------------------------------------------
// Returns UID.
// ---------------------------------------------------------------------------
//
TUint32 CIkev1PluginSession::Uid()
    {
    return iPlugin.Uid();
    }

// ---------------------------------------------------------------------------
// Returns event logger interface.
// ---------------------------------------------------------------------------
//
MKmdEventLoggerIf& CIkev1PluginSession::EventLogger()
    {
    return iPlugin.EventLogger();
    }      

// ---------------------------------------------------------------------------
// Returns internal address (NULL if does not exist). 
// ---------------------------------------------------------------------------
//
CInternalAddress* CIkev1PluginSession::InternalAddressL()
    {
    DEBUG_LOG(_L("CIkev1PluginSession::InternalAddressL"));
    
    CInternalAddress* internalAddress = NULL;
    if ( iInternalAddress != NULL )
        {
        internalAddress = CInternalAddress::NewL( *iInternalAddress );    
        }
    return internalAddress;            
    }

// ---------------------------------------------------------------------------
// From class MIkev1SenderCallback
// Handles completion of sending.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::SendUdpDataCompleted( TInt aStatus )
    {    
    if ( iSendQueue.Count() != 0 )
        {
        // Send queue is not empty.
        // Send next item from queue.
        TIkeSendQueueItem item = iSendQueue[0];
        HBufC8* udpData = item.UdpData();
        TInetAddr destAddr = item.DestAddr();
        TInt localPort = item.LocalPort();
        TUint8 dscp = item.Dscp();
        iSendQueue.Remove(0);
        DoSendUdpData( udpData,
                       destAddr,
                       localPort,
                       dscp );
        
        }
    else
        {
        // IKE message send queue is empty.
        // If session deletion has been requested, complete request.
        DoCompleteDeleteSession( aStatus );
        }
    }

// ---------------------------------------------------------------------------
// Handles PFKEY message.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::PfkeyMessageReceived( const TPfkeyMessage& aPfkeyMessage )
    {
    TRAPD( err, DoPfkeyMessageReceivedL( aPfkeyMessage) );
    err = err;
    DEBUG_LOG1(_L("CIkev1PluginSession::PfkeyMessageReceivedL, err=%d"), err);
    }

// Methods to build and send PFKEY API primitives to IPsec

void CIkev1PluginSession::AcquireSAError( TIpsecSAData& aSAData,
                                          TInt aError )
    {
    iPlugin.AcquireSAError( aSAData, aError );
    }

void CIkev1PluginSession::UpdateSAL( TIpsecSAData& aSaData )
    {
    iPlugin.UpdateSAL( aSaData );
    }

void CIkev1PluginSession::AddSAL( TIpsecSAData& aSaData )
    {
    iPlugin.AddSAL( aSaData );
    }

void CIkev1PluginSession::DeleteIpsecSA( TIpsecSPI& aIpsecSpi )
    {
    iPlugin.DeleteIpsecSA( aIpsecSpi );
    }

void CIkev1PluginSession::DeleteIpsecSA( TUint32 aSPI,
                                         TInetAddr& aSrc, 
                                         TInetAddr& aDst,
                                         TUint8 aProtocol )
    {
    iPlugin.DeleteIpsecSA( aSPI, aSrc, aDst, aProtocol );
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Starts negotiation with a peer. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::NegotiateWithHost( const CIkeData& aIkeData,
                                             TVPNAddress& aInternalAddress,
                                             TRequestStatus& aStatus )
    {
    DEBUG_LOG(_L("CIkev1PluginSession::NegotiateWithHost"));    
    __ASSERT_DEBUG( iClientStatusNegotiate == NULL,
                    User::Invariant() );
    
    // Store client's request status and internal address.
    iClientStatusNegotiate = &aStatus;
    *iClientStatusNegotiate = KRequestPending;
    iClientIaNegotiate = &aInternalAddress;
    
    TRAPD( err, DoNegotiateWithHostL( aIkeData ) );
    
    if ( err != KErrNone )
        {
        DoCompleteNegotiateWithHost( err );
        }
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Cancels negotiate request. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::CancelNegotiateWithHost()
    {    
    DEBUG_LOG(_L("CIkev1PluginSession::CancelNegotiateWithHost"));
    
    if ( iClientStatusNegotiate != NULL )
        {
        // Completion is enough as deletion of session is requested after
        // cancellation.
        DoCompleteNegotiateWithHost( KErrCancel );
        }
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Deletes session. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DeleteSession( const TBool aSilentClose,
                                         TRequestStatus& aStatus )
    {
    DEBUG_LOG1(_L("CIkev1PluginSession::DeleteSession, silent=%d"),
            aSilentClose);
    
    iClientStatusDelete = &aStatus;
    *iClientStatusDelete = KRequestPending;
    
    TBool deactivatingStarted = DeleteSAsWithHost( aSilentClose );    
    if ( !deactivatingStarted )
        {
        DoCompleteDeleteSession( KErrNone );
        }
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Cancels deletion requests. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::CancelDeleteSession()
    {
    DEBUG_LOG(_L("CIkev1PluginSession::CancelDeleteSession"));

    if ( iClientStatusDelete != NULL )
        {
        // Delete SAs silently.
        DeleteSAsWithHost( ETrue );
        
        DoCancelDataTransfer();
        
        User::RequestComplete( iClientStatusDelete, KErrCancel );
        iClientStatusDelete = NULL;
        }
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Requests notification about error condition. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::NotifyError( TRequestStatus& aStatus )
    {
    DEBUG_LOG(_L("CIkev1PluginSession::NotifyError"));
    
    iClientStatusNotifyError = &aStatus;
    *iClientStatusNotifyError = KRequestPending;
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Cancels error notification request. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::CancelNotifyError()
    {
    DEBUG_LOG(_L("CIkev1PluginSession::CancelNotifyError"));
    
    if ( iClientStatusNotifyError != NULL )
        {
        User::RequestComplete( iClientStatusNotifyError, KErrCancel );
        iClientStatusNotifyError = NULL;
        }    
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Requests notification about change of internal address. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::NotifyInternalAddressChanged( TVPNAddress& aInternalAddress,
                                                        TRequestStatus& aStatus )
    {
    DEBUG_LOG(_L("CIkev1PluginSession::NotifyInternalAddressChanged"));
    __ASSERT_DEBUG( iClientStatusNotifyIaChange == NULL,
                    User::Invariant() );
    
    iClientStatusNotifyIaChange = &aStatus;
    *iClientStatusNotifyIaChange = KRequestPending;
    iClientIaNotify = &aInternalAddress;    
    }

// ---------------------------------------------------------------------------
// From class MIkePluginSessionIf
// Cancels internal address change notification request. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::CancelNotifyInternalAddressChanged()
    {    
    DEBUG_LOG(_L("CIkev1PluginSession::CancelNotifyInternalAddressChanged"));
    
    if ( iClientStatusNotifyIaChange != NULL )
        {
        DoCompleteInternalAddressChanged( KErrCancel );
        }
    }

// ---------------------------------------------------------------------------
// From class MIkev1ReceiverCallback
// Handles notification about received IKE message. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::IkeMsgReceivedL( const ThdrISAKMP& aIkeMsg,
                                           const TInetAddr& aSrcAddr,
                                           TInt aLocalPort )
    {
    CIkev1Negotiation* negotiation = NULL;
    TIkev1SAData* sa = NULL;
    
    TUint8 exchange = aIkeMsg.GetExchange();
    negotiation = FindNegotiation( aIkeMsg.GetCookieI(),
                                   aIkeMsg.GetCookieR(),
                                   exchange,
                                   aIkeMsg.GetMessageId() );
    
    if ( exchange == ISAKMP_EXCHANGE_INFO || exchange == ISAKMP_EXCHANGE_TRANSACT )
        {
#ifdef _DEBUG
        if ( exchange == ISAKMP_EXCHANGE_INFO )
            {
            DEBUG_LOG( _L("---ISAKMP_EXCHANGE_INFO message received---") );
            }
        else 
            {
            DEBUG_LOG( _L("---ISAKMP_EXCHANGE_TRANSACTION message received---") );
            }                
#endif
        TBool inactive = EFalse;                 
        if ( !negotiation ) 
            {
            if ( exchange == ISAKMP_EXCHANGE_INFO )
                {
                sa = FindIkev1SAData( aIkeMsg.GetCookieI(),
                                      aIkeMsg.GetCookieR() );
                if ( sa )
                    {
                    negotiation = CIkev1Negotiation::NewL( this,
                                                           iPFKeySocketIf,
                                                           iDebug,
                                                           sa,
                                                           RESPONDER );
                    }       
                }       
            if ( !negotiation )
                {
                DEBUG_LOG( _L("Cannot find a matching negotiation") );
#ifdef _DEBUG                    
                const TPtrC8 ikeMsgPtr( (TUint8 *)&aIkeMsg, (TUint16)aIkeMsg.GetLength() );
                TInetAddr dstAddr;
                GetLocalAddress( dstAddr );
                dstAddr.SetPort( aLocalPort );
                TRACE_MSG_IKEV1( ikeMsgPtr, aSrcAddr, dstAddr );
#endif // _DEBUG                    
                return; //Not found
                }
            CleanupStack::PushL( negotiation );
            inactive = ETrue;   //Not enqueued because not active. Only used to process the packet
            }

        if ( exchange == ISAKMP_EXCHANGE_INFO )
            {                   
            CIkev1InfoNegotiation* info_neg = new (ELeave) CIkev1InfoNegotiation( *this,
                                                                                  *negotiation,
                                                                                  iDebug );
            CleanupStack::PushL( info_neg );            
            // Update the negotiation state
            info_neg->ExecuteL( aIkeMsg,
                                aSrcAddr,
                                aLocalPort  );
            CleanupStack::PopAndDestroy( info_neg );
            
            if ( inactive )
                {   
                CleanupStack::PopAndDestroy( negotiation );
                }
            else
                {
                if ( negotiation->Finished() )
                    {
                    DeleteNegotiation( negotiation );
                    }
                }   
            }
        else
            { 
            //
            // An ISAKMP transaction exchange message received
            // The handling of this requires that there exists a
            // CTransNegotiation object pointer linked into current
            // CIkev1Negotiation object 
            //
            if ( negotiation )
                {
                if ( negotiation->ExecuteTransactionL( aIkeMsg,
                                                       aSrcAddr,
                                                       aLocalPort ) )
                    {
                    if ( negotiation->Finished() )
                        {
                        DeleteNegotiation( negotiation );
                        }
                    }
                else
                    {
                    DEBUG_LOG( _L("Unexpected Transaction excange message") );
#ifdef _DEBUG   
                    const TPtrC8 ikeMsgPtr( (TUint8 *)&aIkeMsg, (TUint16)aIkeMsg.GetLength() );
                    TInetAddr dstAddr;
                    GetLocalAddress( dstAddr );
                    dstAddr.SetPort( aLocalPort );
                    TRACE_MSG_IKEV1( ikeMsgPtr, aSrcAddr, dstAddr );                        
#endif // _DEBUG                        
                    }     
                }      
            }   
        return;
        }

    //
    // IKE Main, Aggressive and Quick mode exchanges
    //
    if ( negotiation )
        {
        negotiation->ExecuteL( aIkeMsg, aSrcAddr, aLocalPort );
        if ( negotiation->Finished() )
            {
            DeleteNegotiation( negotiation );
            }
        return;
        }   
    DEBUG_LOG( _L("No active negotiation found...Searching existing PHASE_II") );

    TBool status;
    sa = FindIkev1SAData( aIkeMsg.GetCookieI(),
                          aIkeMsg.GetCookieR() );
    if ( sa )
        {
        DEBUG_LOG( _L("Creating a NEW IKE Phase 2 Negotiation") );           
        
        TRAPD( err, negotiation = CIkev1Negotiation::NewL( this,
                                                           iPFKeySocketIf,
                                                           iDebug,
                                                           sa,
                                                           RESPONDER ) );
        if ( err == KErrNone )
            {
            CleanupStack::PushL( negotiation );
            status = negotiation->ExecutePhase2L( aIkeMsg, aSrcAddr, aLocalPort ); 
            if ( status && !negotiation->Finished() )
               {
               //Negotiation OK
               CleanupStack::Pop();    //negotiation safe
               return;
               }
            CleanupStack::PopAndDestroy();
            }            
        return;
        }
    else
        {
        TCookie NULL_COOKIE;
        NULL_COOKIE.FillZ(ISAKMP_COOKIE_SIZE);
        if ( aIkeMsg.GetCookieR() == NULL_COOKIE )
            {
            //
            // This is the initial opening message from a remote host
            // Start a new negotiation
            //
            DEBUG_LOG( _L("Creating a NEW IKE Phase 1 Negotiation") );
            TRAPD( err, negotiation = CIkev1Negotiation::NewL( this,
                                                               iPFKeySocketIf,
                                                               iDebug,
                                                               aSrcAddr,
                                                               aIkeMsg.GetCookieI(),
                                                               EFalse ) );
            
            if ( err == KErrNone )
                {
                CleanupStack::PushL( negotiation );
                status = negotiation->ExecuteL( aIkeMsg, aSrcAddr, aLocalPort );                   
                if ( status && !negotiation->Finished() )
                    {
                    //Negotiation OK
                    CleanupStack::Pop();    //negotiation safe
                    return;
                    }
                CleanupStack::PopAndDestroy( negotiation );
                }                
            return;
            }
        }

    if ( !negotiation )
        {
        DEBUG_LOG( _L("Cannot find a matching negotiation") );
#ifdef _DEBUG
        const TPtrC8 ikeMsgPtr((TUint8 *)&aIkeMsg, (TUint16)aIkeMsg.GetLength());
        TInetAddr dstAddr;
        GetLocalAddress( dstAddr );
        dstAddr.SetPort( aLocalPort );
        TRACE_MSG_IKEV1( ikeMsgPtr, aSrcAddr, dstAddr );
#endif // _DEBUG            
        return;
        }
    }

// ---------------------------------------------------------------------------
// From class MIkev1ReceiverCallback
// Handles notification about receive error. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::ReceiveError( TInt aError )
    {
    HandleError( aError );
    }

// ---------------------------------------------------------------------------
// Requests sending of UDP data.
// ---------------------------------------------------------------------------
//    
void CIkev1PluginSession::DoSendUdpDataL( HBufC8* aUdpData,
                                          const TInetAddr& aDestAddr,
                                          TInt aLocalPort,
                                          TUint8 aDscp )
    {
    if ( !iSender->IsActive() &&
         iSendQueue.Count() == 0 )
        {
        // Sending is not in progress and send queue is empty.
        // Start sending UDP data.
        DoSendUdpData( aUdpData,
                       aDestAddr,
                       aLocalPort,
                       aDscp );
        }
    else
        {
        // Store buffer into send queue for later sending.
        TIkeSendQueueItem item = TIkeSendQueueItem( aUdpData,
                                                    aDestAddr,
                                                    aLocalPort,
                                                    aDscp );
        iSendQueue.Append( item );                
        }        
    }

// ---------------------------------------------------------------------------
// Sends UDP data. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoSendUdpData( HBufC8* aUdpData,
                                         const TInetAddr& aDestAddr,
                                         TInt aLocalPort,
                                         TUint8 aDscp )
    {
    __ASSERT_DEBUG( aUdpData != NULL,
                    User::Invariant() );
    
    iSender->SendUdpData( aUdpData, aDestAddr, aLocalPort, aDscp );
    }

// ---------------------------------------------------------------------------
// Handles PFKEY message.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoPfkeyMessageReceivedL( const TPfkeyMessage& aPfkeyMessage )
    {
    //
    //  Process received PFKEY message according to message type
    //
#ifdef _DEBUG    
    TBuf<40> txt_addr;
#endif        
            
    TIkev1SAData* ikev1SAdata = NULL;
    CIkev1Negotiation* negotiation = NULL;   
    
    switch ( aPfkeyMessage.iBase.iMsg->sadb_msg_type )
        {
        case SADB_ACQUIRE:
             //
             // A request to negotiate an IPSEC SA received
             // Try to find an existing IKE SA with remote address 
             //
#ifdef _DEBUG
            aPfkeyMessage.iDstAddr.Address().OutputWithScope( txt_addr );
#endif                
            ikev1SAdata = FindIkev1SADataWithAddr( aPfkeyMessage.iDstAddr.Address() );
            if ( ikev1SAdata )
                {
                //
                // An IKE SA found for Acquire. Get a negotiation
                // object for IKE Quick mode SA exchange 
                //
                negotiation = CIkev1Negotiation::NewL( this,
                                                       iPFKeySocketIf,
                                                       iDebug,
                                                       ikev1SAdata,
                                                       INITIATOR,
                                                       &aPfkeyMessage );                     
                CleanupStack::PushL( negotiation );                   
                negotiation->InitPhase2L();    //Because is initiator
                DEBUG_LOG1( _L("IKEv1 SA found for Acquire IP: %S"), &txt_addr );
                CleanupStack::Pop();                                                
                }
            else
                {
                //
                // No IKE SA found for Acquire.
                //
                // If rekeying is in progress, IKE Quick mode SA exchange
                // is started after Phase I has completed.
                //
                CIkev1Negotiation* negotiation = iFirstNegotiation;
                while ( negotiation != NULL )
                    {
                    if ( negotiation->IsRekeyingIkeSa() )
                        {                        
                        break;
                        }
                    negotiation = negotiation->iNext;
                    }                
                if ( negotiation != NULL )
                    {
                    negotiation->PreparePhase2L( aPfkeyMessage );                    
                    DEBUG_LOG1( _L("Negotiation found for Acquire IP: %S"), &txt_addr );
                    break;
                    }                    
                
                //
                // Otherwise we shall start a new IKE SA negotiation to
                // defined destination address.
                //
                negotiation = CIkev1Negotiation::NewL( this,
                                                       iPFKeySocketIf,
                                                       iDebug,
                                                       (TInetAddr&)aPfkeyMessage.iDstAddr.Address(),
                                                       aPfkeyMessage );
                CleanupStack::PushL( negotiation );
                negotiation->InitNegotiationL();
                if ( negotiation->Finished() )
                    {
                    CleanupStack::PopAndDestroy();
                    }
                else 
                    {
                    CleanupStack::Pop();                                              
                    }
                DEBUG_LOG1( _L("Negotiate a new IKE SA for Acquire IP: %S"), &txt_addr );
                }
            break;

        case SADB_EXPIRE:
            //
            // An IPSEC SA has been expired.
            // Try to find an existing IKE SA with source address
            // (= inbound SA destination address)
            //
#ifdef _DEBUG                
            aPfkeyMessage.iDstAddr.Address().OutputWithScope( txt_addr );
#endif                  
            ikev1SAdata = FindIkev1SAData( aPfkeyMessage.iSrcAddr.Address(),
                                           aPfkeyMessage.iSa.iExt->sadb_sa_spi );
            if ( ikev1SAdata )
                {
                //
                // An IKE SA found for Expire. Get a negotiation
                // object for IKE Informational exchange 
                //
                if ( DeleteIpsecSpi(ikev1SAdata->iSAId,
                                    aPfkeyMessage.iSa.iExt->sadb_sa_spi,
                                    ETrue) )
                    {
                    negotiation = CIkev1Negotiation::NewL( this,
                                                           iPFKeySocketIf,
                                                           iDebug,
                                                           ikev1SAdata,
                                                           RESPONDER );
                    CleanupStack::PushL( negotiation );
                    negotiation->SendDeleteL( aPfkeyMessage.iBase.iMsg->sadb_msg_satype,
                                              aPfkeyMessage.iSa.iExt->sadb_sa_spi );
                    CleanupStack::PopAndDestroy();
                    DEBUG_LOG3(_L("Notifying SGW, IPsec SA Expiration (addr = %S, spi = %x , proto = %d)"), &txt_addr,
                            ByteOrder::Swap32(aPfkeyMessage.iSa.iExt->sadb_sa_spi), aPfkeyMessage.iDstAddr.iExt->sadb_address_proto);
                    }       
                }
            else
                {                       
                DEBUG_LOG1( _L("No IKE SA found Expire IP: %S"), &txt_addr );
                }                       
            break;
             
        default:
            break;
            
        }
    }

// ---------------------------------------------------------------------------
// Deletes IKE SAs. 
// ---------------------------------------------------------------------------
//
TBool CIkev1PluginSession::DeleteSAsWithHost( TBool aSilentClose )
    {        
    DEBUG_LOG( _L("Deactivating IKEv1 SA:s") );
    
    //
    // For sure check if there is any ongoing negotiations for this
    // and delete these negotiations immediatelly.
    //
    while ( iFirstNegotiation )
        {
        CIkev1Negotiation* negotiation = iFirstNegotiation;
        iFirstNegotiation = negotiation->iNext;             
        delete negotiation;
        }
    
    TInt deactivatingStarted( EFalse );
    for ( TInt i=0;i<iIkev1SAs.Count();i++ )
        {
        CIkev1SA* sa = iIkev1SAs[i]; 
        if ( !sa->IsExpired() )
            {
            TIkev1SAData& ikeSaData = sa->iHdr;
            deactivatingStarted = DeleteIkeSA( &ikeSaData, aSilentClose );
            }   
        }

    return deactivatingStarted;
    }    

// ---------------------------------------------------------------------------
// Handles starting of negotiation with a peer. 
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoNegotiateWithHostL( const CIkeData& aIkeData )
    {
    __ASSERT_DEBUG( iIkeData == NULL,
                    User::Invariant() );    
    
    iIkeData = CIkeData::NewL( &aIkeData ); 

    // Start ISAKMP Phase 1 negotiation to the specified gateway immediately
    // if the Internal VPN address feature is enabled in policy (= IA payload
    // or CONFIG-MODE). Otherwise postpone negotiation.    
    if ( !aIkeData.iUseInternalAddr &&
         !aIkeData.iUseCfgMode )
        {
        DEBUG_LOG(_L("Negotiation postponed."));
        User::RequestComplete( iClientStatusNegotiate, KErrNone );
        iClientStatusNegotiate = NULL;
        return;
        }   
    
    CIkev1Negotiation* negotiation = CIkev1Negotiation::NewL( this,
                                                              iPFKeySocketIf,
                                                              iDebug,
                                                              iIkeData->iAddr,
                                                              ETrue );
    CleanupStack::PushL( negotiation );
    negotiation->InitNegotiationL();        
    if ( negotiation->Finished() )
        {
        CleanupStack::PopAndDestroy( negotiation );
        User::Leave( KKmdIkeNegotFailed );            
        }
    else
        {           
        CleanupStack::Pop( negotiation );
        }   
    }  

// ---------------------------------------------------------------------------
// Handles fatal error.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoHandleError( TInt aError )
    {
    DEBUG_LOG1(_L("CIkev1PluginSession::DoHandleError, err=%d"), aError);
    
    while ( iFirstNegotiation )
        {
        CIkev1Negotiation* negotiation = iFirstNegotiation;
        iFirstNegotiation = negotiation->iNext;             
        delete negotiation;
        }
    
    while ( iIkev1SAs.Count() )
        {
        CIkev1SA* ikev1SA = iIkev1SAs[0];
        iIkev1SAs.Remove(0);
        delete ikev1SA;
        }
    
    // Complete client's requests.
    DoCompleteNegotiateWithHost( aError );
    DoCompleteDeleteSession( aError );
    DoCompleteNotifyError( aError );
    }

// ---------------------------------------------------------------------------
// Handles completion of client's negotiate request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoCompleteNegotiateWithHost( TInt aStatus )
    {         
    if ( iClientStatusNegotiate != NULL )
        {
        if ( aStatus == KErrNone )
            {
            if ( iInternalAddress != NULL )
                {
                __ASSERT_DEBUG( iIkeData != NULL,
                                User::Invariant() );
                // Build internal address for client.       
                VPNAddrInfo::BuildVPNAddrInfo( iInternalAddress,
                                               iIkeData->iDnsServer,
                                               *iClientIaNegotiate,
                                               iDebug );
                }
            }

        // Complete client's request.
        DEBUG_LOG1(_L("CIkev1PluginSession::DoCompleteNegotiateWithHost, aStatus=%d"),
                aStatus);        
        User::RequestComplete( iClientStatusNegotiate, aStatus );
        iClientStatusNegotiate = NULL;
        iClientIaNegotiate = NULL;
        }
    }

// ---------------------------------------------------------------------------
// Handles completion of client's delete session request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoCompleteDeleteSession( TInt aStatus )
    {       
    if ( iClientStatusDelete != NULL )
        {
        DoCancelDataTransfer();
        
        DEBUG_LOG1(_L("CIkev1PluginSession::DoCompleteDeleteSession, aStatus=%d"),
                aStatus);
        User::RequestComplete( iClientStatusDelete, aStatus );
        iClientStatusDelete = NULL;
        }    
    }

// ---------------------------------------------------------------------------
// Handles completion of client's notify error request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoCompleteNotifyError( TInt aStatus )
    {   
    if ( iClientStatusNotifyError != NULL )
        {
        DoCancelDataTransfer();

        DEBUG_LOG1(_L("CIkev1PluginSession::DoCompleteNotifyError, aStatus=%d"),
                aStatus);
        User::RequestComplete( iClientStatusNotifyError, aStatus );
        iClientStatusNotifyError = NULL;
        }        
    }

// ---------------------------------------------------------------------------
// Handles completion of client's notify internal address change request.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoCompleteInternalAddressChanged( TInt aStatus )
    {
    if ( iClientStatusNotifyIaChange != NULL )
        {       
        DEBUG_LOG1(_L("CIkev1PluginSession::DoCompleteInternalAddressChange, aStatus=%d"),
                aStatus);

        if ( aStatus == KErrNone )
            {
            if ( iInternalAddress != NULL )
                {
                // Build internal address for client.       
                VPNAddrInfo::BuildVPNAddrInfo( iInternalAddress,
                                               iIkeData->iDnsServer,
                                               *iClientIaNotify,
                                               iDebug );            
                }
            }
        
        if ( aStatus != KErrNone &&
             aStatus != KErrCancel )
            {
            HandleError( aStatus);
            return;
            }
        
        User::RequestComplete( iClientStatusNotifyIaChange, aStatus );
        iClientStatusNotifyIaChange = NULL;
        iClientIaNotify = NULL;
        }
    }

// ---------------------------------------------------------------------------
// Cancels data transfer.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoCancelDataTransfer()
    {
    iReceiver->Cancel();
    iDataInterface.StopReceive();
    DoEmptySendQueue();
    iSender->Cancel();
    }

// ---------------------------------------------------------------------------
// Empties send queue.
// ---------------------------------------------------------------------------
//
void CIkev1PluginSession::DoEmptySendQueue()
    {
    while ( iSendQueue.Count() )
        {
        TIkeSendQueueItem item = iSendQueue[0];
        HBufC8* udpData = item.UdpData();
        iSendQueue.Remove(0);
        delete udpData;
        udpData = NULL;
        }    
    }

