diff -r 000000000000 -r 33413c0669b9 vpnengine/sit/src/vpnconnstarter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/sit/src/vpnconnstarter.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,652 @@ +/* +* Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: VPN connection starter +* +*/ + +#include +#include +#include + +#if defined(__PFX_MIP4__) +#include +#endif + +#include "vpnconnstarter.h" +#include "ikepolparser.h" +#include "ipsecpolparser.h" +#include "log.h" + +// Task handling states +enum TConnStarterState + { + KStateStartRealIap, + KStateRealIapConnected, + KStateGetVpnPolicy, + KStateActivateKmd, + KStateAfterActivateKmd, + KStateLoadIpsecPolicy, + KStateAfterLoadIpsecPolicy, + KStateActivateIpsecPolicy, + KStateAfterActivateIpsecPolicy + }; + + +CVpnConnStarter* CVpnConnStarter::NewL(MTaskHandlerManager* aManager, + const TTaskArrivedEventData& aTaskInfo) + { + LOG(Log::Printf(_L("CVpnConnStarter::NewL - begin\n"))); + CVpnConnStarter* self = new (ELeave) CVpnConnStarter(aManager, aTaskInfo); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(); // self + LOG(Log::Printf(_L("CVpnConnStarter::NewL - end\n"))); + return self; + } + +CVpnConnStarter::CVpnConnStarter(MTaskHandlerManager* aManager, + const TTaskArrivedEventData& aTaskInfo) + : CTaskHandler(aManager, aTaskInfo, EStartVpnConnCancelEvent, &iEventSpecDes), + iVpnAddressInfoDes(iVpnAddressInfo), + iIfInfoDes(iIfInfo) + { + iIfInfo.iRealIapId = 0; + iIfInfo.iRealNetId = 0; + } + +void CVpnConnStarter::ConstructL() + { + CActiveScheduler::Add(this); + User::LeaveIfError(iEventMediator.Connect()); + User::LeaveIfError(iVpnServ.Connect()); + User::LeaveIfError(iKmdServ.Connect()); + User::LeaveIfError(iPolicyServ.Connect()); +#if defined(__PFX_MIP4__) + FeatureManager::InitializeLibL(); +#endif + } + +CVpnConnStarter::~CVpnConnStarter() + { + LOG(Log::Printf(_L("CVpnConnStarter::~CVpnConnStarter\n"))); + Cancel(); + iEventMediator.Close(); + iVpnServ.Close(); + iKmdServ.Close(); + iPolicyServ.Close(); + + delete iIkePolicyData; + delete iIpsecPolicyData; + delete iCustomTs; + + RELEASE_EVENT_LOGGER; +#if defined(__PFX_MIP4__) + FeatureManager::UnInitializeLib(); +#endif + } + +void CVpnConnStarter::StartTaskHandling() + { + // Read input parameters. + // Changing the content of iEventSpecDes is not allowed + iVpnIapId = iEventSpecDes().iIfInfo.iVpnIapId; + GotoState(KStateStartRealIap); + } + +void CVpnConnStarter::ChangeStateL() + { + switch (NextState()) + { + case KStateStartRealIap: + StateStartRealIap(); + break; + + case KStateRealIapConnected: + StateRealIapConnectedL(); + break; + + case KStateGetVpnPolicy: + StateGetVpnPolicyL(); + break; + + case KStateLoadIpsecPolicy: + StateLoadIpsecPolicy(); + break; + + case KStateAfterLoadIpsecPolicy: + StateAfterLoadIpsecPolicyL(); + break; + + case KStateActivateIpsecPolicy: + StateActivateIpsecPolicy(); + break; + + case KStateAfterActivateIpsecPolicy: + StateAfterActivateIpsecPolicyL(); + break; + + case KStateActivateKmd: + StateActivateKmdL(); + break; + + case KStateAfterActivateKmd: + StateAfterActivateKmdL(); + break; + + default: + User::Panic(KSitName, EPanicInvalidTaskHandlerState); + } + } + +void CVpnConnStarter::CancelOngoingOperation() + { + LOG_EVENT(R_VPN_MSG_VPN_IAP_ACT_CANCEL, NULL, 0, 0); + + switch (CurrState()) + { + case KStateStartRealIap: + DEB(LOG(Log::Printf(_L("Canceling start real connection\n") ));) + iKmdServ.CancelStartRealConnection(); + break; + case KStateRealIapConnected: + case KStateGetVpnPolicy: + case KStateAfterActivateKmd: + case KStateAfterLoadIpsecPolicy: + case KStateAfterActivateIpsecPolicy: + // There's no ongoing external request to cancel + break; + + case KStateActivateKmd: + DEB(LOG(Log::Printf(_L("Canceling KMD activate\n") ));) + iKmdServ.CancelActivate(); + break; + + case KStateLoadIpsecPolicy: + DEB(LOG(Log::Printf(_L("Canceling Ipsec policy load\n") ));) + iPolicyServ.CancelLoad(); + break; + + case KStateActivateIpsecPolicy: + DEB(LOG(Log::Printf(_L("Canceling Ipsec policy activate\n") ));) + iPolicyServ.CancelActivate(); + break; + + default: + User::Panic(KSitName, EPanicInvalidTaskHandlerState); + } + + RollbackProcess(); + } + +void CVpnConnStarter::StateStartRealIap() + { + SetCurrState(KStateStartRealIap); + + DEB(LOG(Log::Printf(_L("StateStartRealIap, VPN IAP Id:%d\n"), + iVpnIapId));) + iKmdServ.StartRealConnection(iVpnIapId, iIfInfoDes, iStatus); + SetNextState(KStateRealIapConnected); + SetActive(); + } + +void CVpnConnStarter::StateRealIapConnectedL() + { + SetCurrState(KStateRealIapConnected); + DEB(LOG(Log::Printf(_L("StateRealIapConnected:%d\n"), iStatus.Int()));) + User::LeaveIfError( iStatus.Int() ); + INIT_EVENT_LOGGER(iEventMediator, + iVpnIapId, iIfInfoDes().iRealIapId); + LOG_EVENT(R_VPN_MSG_VPN_IAP_ACT_START, this, 0, 0); + GotoState(KStateGetVpnPolicy); + } + +void CVpnConnStarter::StateGetVpnPolicyL() + { + SetCurrState(KStateGetVpnPolicy); + + HBufC8* vpnPolicyData = NULL; + DEB(LOG(Log::Printf(_L("Starting to get VPN policy: %S\n"), + &(iEventSpecDes().iPolicyId) ));) + // Get VPN policy data + User::LeaveIfError( + iVpnServ.GetPolicyData(iEventSpecDes().iPolicyId, vpnPolicyData)); + CleanupStack::PushL(vpnPolicyData); + + // Convert to 16 bit + HBufC16* vpnPolicyData16 = CSit::To16BitL(*vpnPolicyData); + CleanupStack::PushL(vpnPolicyData16); + + // Buffer containing Nokia NAT-T specific traffic selectors + // if specified in IKE policy + delete iCustomTs; + iCustomTs = NULL; + iCustomTs = HBufC8::NewL(256); + + // Extract IKE and IPSec policies from the VPN policy + // Also stores custom Traffic Selectors if needed + GetIkePolicyFromVpnPolicyL(*vpnPolicyData16); + DEB(LOG(Log::Printf(_L("IKE policy parsing succeeded\n")));) + + GetIpsecPolicyFromVpnPolicyL(*vpnPolicyData16); + DEB(LOG(Log::Printf(_L("Ipsec policy parsing succeeded\n")));) + + // Insert stored traffic selectors to IPsec policy + InsertCustomTrafficSelectorsL(); + + // Custom traffic selectors are no longer needed since + // they were given to IPsec in above + delete iCustomTs; + iCustomTs = NULL; + + CleanupStack::PopAndDestroy(2); // vpnPolicyData16, vpnPolicyData + + GotoState(KStateLoadIpsecPolicy); + } + +void CVpnConnStarter::StateActivateKmdL() + { + SetCurrState(KStateActivateKmd); + + DEB(LOG(Log::Printf(_L("Starting KMD activate\n")));) + iKmdServ.Activate(iVpnIapId, + iEventSpecDes().iIfInfo.iVPNIfName, + *iIkePolicyData, + iVpnAddressInfoDes, + iStatus); + + SetNextState(KStateAfterActivateKmd); + SetActive(); + } + +void CVpnConnStarter::StateAfterActivateKmdL() + { + SetCurrState(KStateAfterActivateKmd); + + DEB(LOG(Log::Printf( + _L("KMD activate completed, status = %d, VPN IAP Id = %d\n"), + iStatus.Int(), iVpnIapId));) + // Leave if KMD activation failed + User::LeaveIfError(iStatus.Int()); + + LOG_EVENT(R_VPN_MSG_VPN_IAP_ACTIVATED, + &(iVpnAddressInfo.iVPNIfAddr), iStatus.Int(), 0); + + TaskComplete(KErrNone); + } + +void CVpnConnStarter::StateLoadIpsecPolicy() + { + SetCurrState(KStateLoadIpsecPolicy); + + DEB(LOG(Log::Printf(_L("Starting to load Ipsec policy\n")));) + LoadIpsecPolicy(); + + SetNextState(KStateAfterLoadIpsecPolicy); + SetActive(); + } + +void CVpnConnStarter::StateAfterLoadIpsecPolicyL() + { + SetCurrState(KStateAfterLoadIpsecPolicy); + DEB(LOG(Log::Printf( + _L("Ipsec policy load completed, status = %d\n"), iStatus.Int()));) + // Log some debug data if policy loading failed + if (iStatus.Int() == ESelectorConflict) + { + DEB(TPolicyNameInfo conflictInfo;) + DEB(TInt err = iPolicyServ.GetDebugInfo(conflictInfo, KConflictingPolicyInfo)); + + DEB(if (!err)) + DEB(LOG(Log::Printf(_L("Additional debug info: %S\n"), &conflictInfo));) + } + + if (iStatus.Int() == EParsingError) + { + DEB(typedef TBuf<200> TParsingErrorInfo;) + DEB(TParsingErrorInfo errorInfo;) + DEB(TInt err = iPolicyServ.GetDebugInfo(errorInfo, KParsingErrorInfo)); + + DEB(if (!err)) + DEB(LOG(Log::Printf(_L("Additional debug info: %S\n"), &errorInfo));) + } + + // Leave if policy loading failed + User::LeaveIfError(iStatus.Int()); + + GotoState(KStateActivateIpsecPolicy); + } + +void CVpnConnStarter::StateActivateIpsecPolicy() + { + SetCurrState(KStateActivateIpsecPolicy); + + DEB(LOG(Log::Printf(_L("Activating Ipsec policy, handle = %d\n"), iIpsecPolicyHandleDes()));) + iPolicyServ.ActivatePolicy(iIpsecPolicyHandleDes(), iStatus); + + SetNextState(KStateAfterActivateIpsecPolicy); + SetActive(); + } + +void CVpnConnStarter::StateAfterActivateIpsecPolicyL() + { + DEB(LOG(Log::Printf(_L("Ipsec policy activated, status = %d\n"), iStatus.Int()));) + + User::LeaveIfError(iStatus.Int()); + + GotoState(KStateActivateKmd); + } + +void CVpnConnStarter::ReportResult(TInt aStatus) + { + DEB(LOG(Log::Printf(_L("CVpnConnStarter::ReportResult\n")));) + // Report the startup result with associated + // information to the Event Mediator + TStartVpnConnEventData eventData; + + eventData.iTaskStatus = aStatus; + + if (aStatus == KErrNone) + { + eventData.iIkePolicyHandle.iHandle = iVpnIapId; + eventData.iIpsecPolicyHandle = iIpsecPolicyHandleDes(); + eventData.iVpnAddressInfo = iVpnAddressInfo; + eventData.iIapId = iIfInfo.iRealIapId; + eventData.iNetId = iIfInfo.iRealNetId; + DEB(LOG(Log::Printf(_L("Iap:%d Net:%d\n"), + eventData.iIapId, eventData.iNetId));) + } + + TPckg eventDataDes(eventData); + + ReportEvent(EStartVpnConnEvent, iEventSpecDes, eventDataDes); + + LOG_EVENT(R_VPN_MSG_VPN_IAP_ACT_END, + &(iEventSpecDes().iPolicyId), aStatus, 0); + } + +TInt CVpnConnStarter::RunError(TInt aError) + { + LOG_EVENT(R_VPN_MSG_VPN_IAP_ACT_FAILED, NULL, aError, 0); + + RollbackProcess(); + TaskComplete(aError); + return KErrNone; + } + +void CVpnConnStarter::RollbackProcess() + { + // If the VPN connection activation process + // fails at some point, we may need to reverse + // some of the actions that we've done: + // - If the KMD has been activated (the IKE policy has been loaded), + // deactivate KMD (unload IKE policy) + // - If the IPSec policy has been loaded and activated, + // unload the policy + // No actions are needed regarding the real IAP connection + // as it will be closed by the system in the lack of users. + + DEB(LOG(Log::Printf(_L("Deactivating IKE policy due VPN activation error, VPN IAP Id = %d\n"),iVpnIapId));) + iKmdServ.StopVpnConnection(iVpnIapId, TKmdStopConnection::ENormal); + + if (iIpsecPolicyHandleDes().iHandle != 0) + { + DEB(LOG(Log::Printf(_L("Unloading Ipsec policy due VPN activation error, VPN IAP Id = %d\n"),iVpnIapId));) + TRequestStatus status; + iPolicyServ.UnloadPolicy(iIpsecPolicyHandleDes(), status); + User::WaitForRequest(status); + } + } + +void CVpnConnStarter::ResolveIpsecFQDNL(CSecPolBundleList* aSecPolBundleList) + { + if (aSecPolBundleList) + { + for (TInt i(0); iCount(); ++i) + { + CSecpolBundleItem* item = aSecPolBundleList->At(i); + HBufC* dnsname = item->iTunnelEpFQDN; + + TInetAddr result = ResolveFQDNL(*dnsname); + // 39 for ipv6 + TBuf<39> ipAddress; + result.Output( ipAddress ); + item->iTunnel.Input(ipAddress); + DEB(LOG(Log::Printf(_L("Resolved:%S\n"), &ipAddress));) + } + } + } + +void CVpnConnStarter::ResolveIkeFQDNL(CIkeData* aIkeData) + { + ASSERT(aIkeData); + if (aIkeData->iFQDNAddr) + { + TInetAddr result = ResolveFQDNL(aIkeData->iFQDNAddr->GetData()); + // 39 for ipv6 + TBuf<39> ipAddress; + result.Output( ipAddress ); + aIkeData->iAddr.Input(ipAddress); + DEB(LOG(Log::Printf(_L("Resolved:%S\n"), &ipAddress));) + } + } + +TInetAddr CVpnConnStarter::ResolveFQDNL(const TDesC& aAddress) + { + DEB(LOG(Log::Printf(_L("ResolveFQDNL:%S\n"), &aAddress));) + TRequestStatus status; + TNameEntry result; + iKmdServ.ResolveAddress(iVpnIapId, aAddress, result, status); + User::WaitForRequest(status); + User::LeaveIfError(status.Int()); + TInetAddr address(result().iAddr); + return address; + } + +void CVpnConnStarter::FormCustomTrafficSelectorsL(CIkeData *aIkeData) + { + // Add Nokia NAT-T specific traffic selectors to IPsec + // custom traffic selector buffer + if (aIkeData->iUseNatProbing) + { + // Default port for Nokia NAT-T + TInt port(KNokiaNattDefaultPort); + if (aIkeData->iEspUdpPort) + port = aIkeData->iEspUdpPort; + + TBuf8<39> addr, mask; + TBuf<39> buffer; + aIkeData->iAddr.Output(buffer); + addr.Copy(buffer); + aIkeData->iMask.Output(buffer); + mask.Copy(buffer); + + HBufC8 *tsBuffer = HBufC8::NewLC(128); + tsBuffer->Des().Format(_L8("remote %S %S local_port %d = { }\n"), + &addr, &mask, port); + DEB(LOG(Log::Printf(_L8("Custom TS:%S\n"), tsBuffer));) + // Make sure that TS fits in the ts buffer + if (iCustomTs->Des().MaxLength() < iCustomTs->Length() + tsBuffer->Length()) + { + iCustomTs = iCustomTs->ReAllocL(iCustomTs->Length() + tsBuffer->Length()); + } + iCustomTs->Des().Append(*tsBuffer); + CleanupStack::PopAndDestroy(tsBuffer); + } + } + +void CVpnConnStarter::InsertCustomTrafficSelectorsL() + { + _LIT8(KSearchTerm, "[POLICY]\n"); + TInt pos = iIpsecPolicyData->Find(KSearchTerm); + if (pos != KErrNotFound) + { + HBufC8 *newIpsecPolicyData = HBufC8::NewL( + iIpsecPolicyData->Length() + iCustomTs->Length()); + pos += KSearchTerm().Length(); + + // Add data to "[POLICY]\n" + newIpsecPolicyData->Des().Append(iIpsecPolicyData->Des().Left(pos)); + + // Add custom traffic selectors + newIpsecPolicyData->Des().Append(*iCustomTs); + + // Add rest of the original data + newIpsecPolicyData->Des().Append(iIpsecPolicyData->Des().Right( + iIpsecPolicyData->Length() - pos)); + + delete iIpsecPolicyData; + iIpsecPolicyData = newIpsecPolicyData; + + DEB(LOG(Log::Write(*iIpsecPolicyData))); + } + } + +void CVpnConnStarter::GetIkePolicyFromVpnPolicyL(const TDesC& aVpnPolicy) + { + CIkeDataArray* ikeList = CIkeDataArray::NewL(1); + CleanupStack::PushL(ikeList); + + TIkeParser ikeParser(aVpnPolicy); + ikeParser.ParseIKESectionsL(ikeList); + + iIkePolicyData = HBufC8::NewL(KIkePolicyBufferSizeIncrement); + const TInt maxRetrans(8); + const TInt maxTraceFileSize(16); + User::LeaveIfError(ikeParser.GeneralInfoWrite(ikeList->iMaxLifetimeSec, + ikeList->iMaxLifetimeKB, + maxRetrans, + maxTraceFileSize, + iIkePolicyData)); + + for (TInt i(0); i < ikeList->Count(); i++) + { + CIkeData* ikeData = ikeList->At(i); + ResolveIkeFQDNL(ikeData); + FormCustomTrafficSelectorsL(ikeData); + User::LeaveIfError(ikeParser.MainWrite(ikeData, iIkePolicyData)); + } + + CleanupStack::PopAndDestroy(ikeList); + } + +void CVpnConnStarter::GetIpsecPolicyFromVpnPolicyL(const TDesC& aVpnPolicy) + { + CIpSecurityPiece* pieceData = new (ELeave) CIpSecurityPiece; + CleanupStack::PushL(pieceData); + pieceData->ConstructL(); + + TIpSecParser parser(aVpnPolicy); + + TInt ret = parser.ParseAndIgnoreIKEL(pieceData); + + if (ret != KErrNone) + { + HBufC* errorInfo = HBufC16::NewL(200); + errorInfo->Des().Copy(pieceData->iErrorInfo); + LOG(TPtr ptr = errorInfo->Des(); Log::Printf(_L("Parsing error info: %S\n"), &ptr)); + delete errorInfo; + CleanupStack::PopAndDestroy(); // pieceData + User::Leave(ret); + } + + ResolveIpsecFQDNL(pieceData->FQDNAddressListL()); + + iIpsecPolicyData = HBufC8::NewL(KPolicyBufferSizeIncrement); + ret = parser.Write(pieceData, iIpsecPolicyData); + + if (ret != KErrNone) + { + LOG(Log::Printf(_L("Ipsec policy write error: %d\n"), ret)); + CleanupStack::PopAndDestroy(); // pieceData + User::Leave(ret); + } + + CleanupStack::PopAndDestroy(); // pieceData + } + +void CVpnConnStarter::LoadIpsecPolicy() + { + // Specify network IDs for tunnel end-point and selector definitions + iZoneInfoSetDes().iSelectorZone.iScope = KScopeNetwork; + iZoneInfoSetDes().iSelectorZone.iId = iEventSpecDes().iIfInfo.iVpnNetId; + iZoneInfoSetDes().iEndPointZone.iScope = KScopeNetwork; + iZoneInfoSetDes().iEndPointZone.iId = iIfInfo.iRealNetId; + + LOG(Log::Printf(_L("CVpnConnStarter::LoadIpsecPolicy - VPN NET ID=%d, Real NET ID=%d\n"), + iEventSpecDes().iIfInfo.iVpnNetId, iIfInfo.iRealNetId )); + +#if defined(__PFX_MIP4__) + + if (IsMip4FeaturePresent()) + { + LOG(Log::Printf(_L("CVpnConnStarter::LoadIpsecPolicy - MIPv4 present, adding MIPv4 bypass selectors\n"))); + iProcessingFlags = KAddIkeBypassSelectors | KAddMip4BypassSelectors; + } + else + { + LOG(Log::Printf(_L("CVpnConnStarter::LoadIpsecPolicy - MIPv4 NOT present, NOT adding MIPv4 bypass selectors\n"))); + iProcessingFlags = KAddIkeBypassSelectors; + } + +#else + + LOG(Log::Printf(_L("CVpnConnStarter::LoadIpsecPolicy - MIPv4 NOT present, NOT adding MIPv4 bypass selectors\n"))); + iProcessingFlags = KAddIkeBypassSelectors; + +#endif // __PFX_MIP4__ + + // Check from commdb wether "real" interface uses DHCP + // and if it does, KAddDhcpBypassSelectors is added to + // the processing flags + + TBool dhcpEnabled = EFalse; + TRAPD(err, IsDhcpEnabledL(dhcpEnabled)); + + if (!err && dhcpEnabled) + { + iProcessingFlags |= KAddDhcpBypassSelectors; + } + + iPolicyServ.LoadPolicy(*iIpsecPolicyData, iIpsecPolicyHandleDes, + iStatus, iZoneInfoSetDes, iProcessingFlags); + } + +void CVpnConnStarter::IsDhcpEnabledL(TBool& aDhcpEnabled) + { + using namespace CMManager; + + RCmManagerExt cmManagerExt; + cmManagerExt.OpenL(); + CleanupClosePushL(cmManagerExt); + + RCmConnectionMethodExt connectionMethod = cmManagerExt.ConnectionMethodL( iIfInfo.iRealIapId ); + CleanupClosePushL(connectionMethod); + + aDhcpEnabled = connectionMethod.GetBoolAttributeL(ECmIPAddFromServer); + if (!aDhcpEnabled) + { + aDhcpEnabled = connectionMethod.GetBoolAttributeL(ECmIPDNSAddrFromServer); + } + + CleanupStack::PopAndDestroy(2); + } + +#if defined(__PFX_MIP4__) + +TBool CVpnConnStarter::IsMip4FeaturePresent(void) + { + return FeatureManager::FeatureSupported(KFeatureIdMIPv4); + } + +#endif // __PFX_MIP4__