diff -r 000000000000 -r af10295192d8 networkprotocols/ipeventnotifier/src/ipeventnotifier.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networkprotocols/ipeventnotifier/src/ipeventnotifier.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,796 @@ +// Copyright (c) 2004-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: +// + +/** + @file ipeventnotifier.cpp + @internalComponent +*/ + + +#include +#include +#include +#include +#include "in6_opt.h" + +#include "ipeventnotifier.h" +#include "HookLog.h" +#include +#include "ipeventnotifierinterface.h" +#include "ipeventlistener.h" +#include +#include "DHCPUnicastTranslator.h" + + +CIPEventNotifier* CIPEventNotifier::NewL() + { + CIPEventNotifier* self = new(ELeave) CIPEventNotifier(); + CleanupStack::PushL(self); + + self->ConstructL(); + + CleanupStack::Pop(); + return self; + } + + + +void CIPEventNotifier::ConstructL() + { + /** + * Create the address/interface listener. + */ + iIPEventListener = new (ELeave) CIPEventListener(*this); + } + + +CIPEventNotifier::CIPEventNotifier() + { + } + + + +CIPEventNotifier::~CIPEventNotifier() + { + iInterfaces.ResetAndDestroy(); + + delete iIPEventListener; + + delete iDHCPUnicastTranslator; + } + + + +void CIPEventNotifier::BindL(CProtocolBase* aProtocol, TUint aId) + { + // Do sanity checks + // + if ((aId != KProtocolInet6Ip) || (aProtocol == this)) + { + User::Leave(KErrArgument); + } + + TUint ourId; + { + TServerProtocolDesc info; + Identify(&info); + ourId = info.iProtocol; + } + + if (aId == ourId) + { + User::Leave(KErrArgument); + } + + if ( iProtocolIPv6 != NULL ) + { + if ( iProtocolIPv6 == aProtocol ) + { + // We don't need to bind to the same protocol twice. + // + return; + } + else + { + // We don't want to bind to a different protocol either... + // + User::Leave(KErrAlreadyExists); + } + } + + iProtocolIPv6 = (CProtocolInet6Binder*) aProtocol; + + // Get a pointer to the event service. + // + ObtainEventService(); + + // Must always register hooks on startup, til the required + // initial values have been received. + // + RegisterHooksL(); + + // Create cache of interfaces known to IP stack + // + AddKnownInterfacesL(); + + /** + * Create owned hook to allow unicast DHCP packets to pass. + * Notice we are bypassing the standard mechanism for including + * another protocol hook (esk files), and are directly creating + * it here as it is needed for the same lifetime as IPEN itself. + */ + if (iDHCPUnicastTranslator == NULL) + { + iDHCPUnicastTranslator = new (ELeave) CDHCPUnicastTranslator(*iProtocolIPv6); + } + // connect the hook to the stack.. + iProtocolIPv6->BindL(iDHCPUnicastTranslator, BindForwardHook()); + + } + + + +void CIPEventNotifier::Unbind(CProtocolBase* aProtocol, TUint /* aId */) + { + if (iProtocolIPv6 != aProtocol) + { + return; + } + + iProtocolIPv6->Unbind(iDHCPUnicastTranslator, 0); + + UnregisterHooks(); + + iProtocolIPv6 = 0; + iEventService = 0; + } + + + +TInt CIPEventNotifier::GetOption(TUint aLevel, TUint aName,TDes8& aOption,CProtocolBase* aSourceProtocol) +/** + * Performs one of the following actions, depending on the option name: + * + * KHandleAttach: Sets up IPEN to catch and publish events on the interface named in aOption. + * Session handle TUint (the pubsub address key) returned in first 4 bytes of aOption buffer. + * + * KHandleRelease: Tells IPEN to stop catching and publishing events to the given session handle + * Handle passed in as TUint in first 4 bytes of aOption buffer. + * + * @param aLevel: should be KSolInetIp for this protocol + * @param aName: see above + * @param aOption: in/out, dependent on option name. See above + * @return error status + * + */ + { + (void) aLevel; + (void) aSourceProtocol; + + TInt retVal = KErrNotSupported; + + switch (aName) + { + case NetMessages::KHandleAttach: + { + TName buf; + buf.Copy( aOption ); + TUint *out_handlenum = (TUint *)aOption.Ptr(); + TRAPD(retval, *out_handlenum = OpenSessionL(buf)); + return retval; + } + + case NetMessages::KHandleRelease: + { + TUint *in_handle = (TUint *)aOption.Ptr(); + TInt ret = CloseSession(*in_handle); + *in_handle = 0; + return ret; + } + + default: + break; + } + + return retVal ; + } + + + +TUint CIPEventNotifier::OpenSessionL(const TDesC& aIfName) +/** + * Sets up IPEN to catch and publish events on the interface named by aIfName. + * Session handle TUint (the pubsub address key) is returned. + * + * @internalTechnology + */ + { + // The IP stack's first interface, a dummy, has an empty name string. + // Therefore an empty string to open against would match this dummy + // interface! So we should report failure if a naughty client is trying to + // monitor events on an interface with an empty name string. + // + if(aIfName == KNullDesC) + { + LOG( + _LIT(KHookNotifyStr,"ERROR: client attempted to monitor interface with empty name"); + HookLog::Printf( KHookNotifyStr ); + ) + User::Leave(KErrBadName); + } + + TInt ifPos = FindPositionOfInterfaceByName(aIfName); + + CIPEventNotifierInterface* iface = 0; + + if(ifPos == KErrNotFound) + { + AddKnownInterfacesL(); + ifPos = FindPositionOfInterfaceByName(aIfName); + User::LeaveIfError(ifPos); + } + + iface = iInterfaces[ifPos]; + + if( AnyClientsCurrentlyInterested() == false ) + { + RegisterHooksL(); + } + + __ASSERT_ALWAYS(iface , User::Leave(KErrCorrupt)); + iface->IncreaseRefCount(); + + PublishLastKnownStates(iface); + + LOG( + _LIT(KHookNotifyStr2,"CIPEventNotifier::OpenSessionL - now monitoring interface %S (pos #%d)"); + HookLog::Printf(KHookNotifyStr2,&aIfName,ifPos); + ) + + return iface->GetInterfaceIndex(); // later we might create a session id and map it to i/f # + } + + +TInt CIPEventNotifier::CloseSession(TUint aHandle) + { + TInt acIfPos = FindPositionOfInterface(aHandle); + + if(acIfPos == KErrNotFound) + { + return KErrNotFound; + } + + CIPEventNotifierInterface* acIf = iInterfaces[acIfPos]; + + acIf->DecreaseRefCount(); + + // shouldn't be more closes than news! naughty client. + __ASSERT_ALWAYS((acIf->GetRefCount() >= 0), User::Panic(_L("IPEN:CloseSession"),1)); + + LOG( + _LIT(KHookNotifyStr2,"CIPEventNotifier::CloseSession handle %d (pos #%d)"); + HookLog::Printf(KHookNotifyStr2,aHandle,acIfPos); + ) + + if( AnyClientsCurrentlyInterested() == false ) + { + UnregisterHooks(); + } + + return KErrNone; + } + + + +void CIPEventNotifier::AddInterfaceL(TUint32 aIfIndex, const TDesC& aIfName) + { + CIPEventNotifierInterface* newActIf = new (ELeave)CIPEventNotifierInterface(aIfIndex, aIfName); + CleanupStack::PushL( newActIf ); + iInterfaces.AppendL(newActIf); + CleanupStack::Pop( newActIf ); + } + + + +void CIPEventNotifier::AddKnownInterfacesL() + { + TUint bufsize = 2048; + HBufC8 *buffer = 0; + TInt exceed; + buffer = GetBuffer(buffer, bufsize); + User::LeaveIfNull(buffer); + TPtr8 bufdes = buffer->Des(); + + do + { + //-- get list of network interfaces + // if exceed>0, all interface could not fit into the buffer. + // In that case allocate a bigger buffer and retry. + // There should be no reason for this while loop to take more than 2 rounds. + bufdes.Set(buffer->Des()); + exceed = iProtocolIPv6->GetOption(KSolInetIfQuery, KSoInetInterfaceInfo, bufdes); + if(exceed > 0) + { + bufsize += exceed * sizeof(TInetInterfaceInfo); + buffer = GetBuffer(buffer, bufsize); + User::LeaveIfNull(buffer); + } + } while (exceed > 0); + + TOverlayArray infoIface(bufdes); + + TInt idx; + for(idx=0; idx < infoIface.Length(); ++idx) + { + TInetInterfaceInfo& iface = infoIface[idx]; + + if(FindPositionOfInterface(iface.iIndex) == KErrNotFound) + { + LOG( + TBuf<2048> tmpBuf; + tmpBuf.Format(_L("Detected new i/f: index:%d, name: "),iface.iIndex); + tmpBuf.Append(iface.iName); + tmpBuf.AppendFormat(_L(", state:%d, SMti:%d, RMtu:%d, SpeedMetric:%d, feat:%d,"), + iface.iState,iface.iSMtu,iface.iRMtu,iface.iSpeedMetric,iface.iFeatures/*,iface.iHwAddr*/); + HookLog::Printf(tmpBuf); + ) + + TRAP_IGNORE(AddInterfaceL(iface.iIndex,iface.iName)); + } + } + LOG(LogKnownInterfaces()); + delete buffer; + } + + +HBufC8* CIPEventNotifier::GetBuffer(HBufC8* apBuf, TInt aBufLenRequired) + { + HBufC8* pBufResult = NULL; + + if(aBufLenRequired <= 0 ) + return NULL; //-- invalid argument + + if(!apBuf) + {//-- the buffer is not allocated at all, try allocate it + pBufResult = HBufC8::New(aBufLenRequired); + } + else + {//-- the buffer is already allocated. Check its size and try to reallocate if + //-- required size is bigger than existing. Otherwise do nothing. + + if(apBuf->Des().MaxSize() >= aBufLenRequired) + pBufResult = apBuf; //-- do nothing, existing buffer is big enough + else + { //-- delete and allocate new buffer to avoid data copying + delete apBuf; + pBufResult = HBufC8::New(aBufLenRequired); + } + } + + return pBufResult; + } + + +TInt CIPEventNotifier::FindPositionOfInterface(TUint32 aIfIndex) const + { + TInt i; + for( i=0 ; iGetInterfaceIndex() == aIfIndex) + { + return i; + } + } + return KErrNotFound; + } + + + +TInt CIPEventNotifier::FindPositionOfInterfaceByName(const TDesC& aIfName) const + { + TInt i; + for( i=0 ; iGetInterfaceName() == aIfName) + { + return i; + } + } + + return KErrNotFound; + } + + +CIPEventNotifierInterface* CIPEventNotifier::GetInterfaceWithIndexL(TInt aIpIfIndex) + { + TInt aIfPos; + aIfPos = FindPositionOfInterface(aIpIfIndex); + if(aIfPos < 0) + { + LOG ( + _LIT(KHookNotifyStr,"ERROR: couldn't find interface %d. err %d"); + HookLog::Printf( KHookNotifyStr,aIpIfIndex, aIfPos); + ) + User::Leave(aIfPos); + } + + return iInterfaces[aIfPos]; + } + + +TBool CIPEventNotifier::AnyClientsCurrentlyInterested(void) const + { +#ifdef ALWAYS_PUBLISH_IP_EVENTS + return true; +#else + TInt i; + for( i=0 ; iGetRefCount() != 0 || + iInterfaces[i]->iMFlagReceived == 0 || + iInterfaces[i]->iLinklocalAddressKnown == 0 ) + // (We must always listen until we've received the M flag and a LL addr. + // This is because DHCP must have access to these events, even if it hasn't started yet.) + { + return true; + } + } + return false; +#endif + } + + +TBool CIPEventNotifier::ShouldPublishEvent(TUint aInterfaceIndex) + { + TInt aIfPos; + aIfPos = FindPositionOfInterface(aInterfaceIndex); + if(aIfPos == KErrNotFound) + { + LOG ( HookLog::Printf(_L("UNEXPECTED: Interface %d not yet known. Trying to find it"),aInterfaceIndex); ) + TRAP_IGNORE(AddKnownInterfacesL()) + aIfPos = FindPositionOfInterface(aInterfaceIndex); + if(aIfPos == KErrNotFound) + { + LOG ( HookLog::Printf(_L("VERY UNEXPECTED: Couldn't add interface %d"),aInterfaceIndex); ) + return EFalse; + } + aIfPos = FindPositionOfInterface(aInterfaceIndex); + } + +#ifdef ALWAYS_PUBLISH_IP_EVENTS + return ETrue; +#else + CIPEventNotifierInterface* aIf = iInterfaces[aIfPos]; + // if the M flag / LL addr has not yet been received we should try to catch it + return( (aIf->iMFlagReceived == 0) || + (aIf->iLinklocalAddressKnown == 0) || + (aIf->GetRefCount() > 0) ); + +#endif + } + + +void CIPEventNotifier::InterfaceDetached(const TDesC & aName, CNifIfBase *aIf) + { + (void)aIf; + TInt pos = FindPositionOfInterfaceByName(aName); + LOG( + _LIT(KHookNotifyStr,"CIPEventNotifier::InterfaceDetached %S ptr=%08x [pos %d of %d interfaces]"); + HookLog::Printf(KHookNotifyStr,&aName,aIf,pos,iInterfaces.Count()); + ) + if(pos != KErrNotFound) + { + CIPEventNotifierInterface* notIf = iInterfaces[pos]; + delete notIf; + iInterfaces.Remove(pos); + } + LOG(LogKnownInterfaces()); + } + +#ifdef _DEBUG +void CIPEventNotifier::LogKnownInterfaces() + { + LOG( + _LIT(KHookNotifyHeader,"Dumping iInterfaces:"); + HookLog::Printf(KHookNotifyHeader); + ) + + for(TInt i=0 ; iGetInterfaceName(); + TUint32 idx = iInterfaces[i]->GetInterfaceIndex(); + HookLog::Printf(KHookNotifyStr, i, &name, idx); + ) + } + } +#endif + + +void CIPEventNotifier::RegisterHooksL(void) +/** + * Registers the hook to catch IP stack events + */ + { + if(!(iProtocolIPv6 && iEventService && iIPEventListener)) + { + User::Leave(KErrNotReady); + } + + // bind to IP for incoming packets + iProtocolIPv6->BindL((CProtocolBase*)this, BindHookAll()); + + + // Register our listener class with the event service. We're interested in: + // + // i. address events + // for determining when DAD has completed and when link-local addresses have been assigned + // to an interface. + // ii. interface events to allow the notifier to associate interface names with the stack's + // internal indices. + // + iEventService->RegisterListener(iIPEventListener, EClassAddress); + iEventService->RegisterListener(iIPEventListener, EClassInterface); + } + + + +void CIPEventNotifier::UnregisterHooks(void) +/** + * Detaches this hook from the running of the stack. + * This is so IPEN doesn't slow down the stack while it isn't + * needed by any clients. + * + * e.g. without this mechanism, every incoming packet would pass through + * CIPEventNotifier::ApplyL pointlessly. + * + */ + { + if(!(iProtocolIPv6 && iEventService && iIPEventListener)) + { + return; + } + + // If IPv6 exists, unbind the hook. Covers both in/out hooks (I hope) + iProtocolIPv6->Unbind(this); + + // Unregister the address/interface listener. + iEventService->RemoveListener(iIPEventListener); + } + + + +void CIPEventNotifier::FillIdentification(TServerProtocolDesc& anEntry) +/** + * Fills in an existing protocol description structure with details of this protocol. + * + * @param aProtocolDesc: reference to the structure to be filled in + */ + { + anEntry.iName=_S("ipen"); + anEntry.iAddrFamily=KAfInet; //KAfExain; + anEntry.iSockType=KSockDatagram; + anEntry.iProtocol=KProtocolId; + anEntry.iVersion=TVersion(1, 0, 0); + anEntry.iByteOrder=EBigEndian; + anEntry.iServiceInfo=KSIDatagram | KSIConnectionLess; + anEntry.iNamingServices=0; + anEntry.iSecurity=KSocketNoSecurity; + anEntry.iMessageSize=0xffff; + anEntry.iServiceTypeInfo=0; // 1 for ability to create sockets + anEntry.iNumSockets=KUnlimitedSockets; + } + + +void CIPEventNotifier::Identify(TServerProtocolDesc* aProtocolDesc) const +/** + * Fills in an existing protocol description structure with details of this protocol. + * + * @param aProtocolDesc: pointer to the structure to be filled in + */ + { + FillIdentification(*aProtocolDesc); + } + + +TInt CIPEventNotifier::DefineProperty(Meta::SMetaData& aEvent, TUint aHandle) + { + TSecurityPolicy readPol(TSecurityPolicy::EAlwaysPass); + _LIT_SECURE_ID(ipeventsid,0x102045B9); + _LIT_SECURITY_POLICY_S0(writePol,ipeventsid); + + TInt err = iPublisher.Define(TUid::Uid(aEvent.GetTypeId().iType), aHandle, + RProperty::EByteArray/*, readPol, writePol*/); + + // Does not matter if property has already been defined - we ignore the error. + if( err == KErrAlreadyExists ) + { + err = KErrNone; + } + + return err; + } + + +void CIPEventNotifier::PublishToHandle(TUint aHandle, Meta::SMetaData& aData) + { + TBuf8 buf; + + TInt res = aData.Store(buf); + if(res != KErrNone) + { + LOG(HookLog::Printf( _L("IPEN can't store: %d"), res)); + return; + } + + TPtrC8 toSet = buf; + res = iPublisher.Set(TUid::Uid(aData.GetTypeId().iType), aHandle, toSet); + if(res != KErrNone) + { + LOG(HookLog::Printf( _L("IPEN can't publish: %d"), res)); + } + } + + + +/** + * Incoming packet + */ +TInt CIPEventNotifier::ApplyL(RMBufHookPacket& aPacket, RMBufRecvInfo& aInfo) + { + + LOG ( + _LIT(KHookNotifyStr,"CIPEventNotifier::ApplyL hit with protocol: %d"); + HookLog::Printf( KHookNotifyStr , aInfo.iProtocol ); + ) + + if(aInfo.iProtocol == static_cast(KProtocolInet6Icmp) && // quickest first + ShouldPublishEvent(aInfo.iInterfaceIndex) ) + { + + LOG ( + _LIT(KHookNotifyStr," ICMPv6 packet detected"); + HookLog::Printf( KHookNotifyStr ); + ) + + /** + * Could switch on ICMP type here, in case the hook needs to catch + * other ICMP packet types.. + */ + TInet6Checksum icmp(aPacket,aInfo.iOffset); + + if(icmp.iHdr) + { + if (icmp.iHdr->Type() == KInet6ICMP_RouterAdv && + icmp.VerifyChecksum( aPacket, + aInfo.iProtocol == static_cast(KProtocolInet6Icmp) ? &aInfo : NULL, + aInfo.iOffset) ) + { + TBool mflag = (icmp.iHdr->M() ? ETrue : EFalse) ; + TBool oflag = (icmp.iHdr->O() ? ETrue : EFalse) ; + + LOG ( + _LIT(KHookNotifyStr," IP EVENT on if# %d: Publishing M flag: %d"); + HookLog::Printf( KHookNotifyStr, aInfo.iInterfaceIndex, mflag ); + _LIT(KHookOFlagNotifyStr," IP EVENT on if# %d: Publishing O flag: %d"); + HookLog::Printf( KHookOFlagNotifyStr, aInfo.iInterfaceIndex, oflag ); + ) + + TRAPD(err, PublishMFlagL(aInfo.iInterfaceIndex, mflag, oflag)); + if(err!=KErrNone) + { + LOG ( + _LIT(KLogStr, "Can't publish M and O flags message! Err %d\n"); + HookLog::Printf(KLogStr, err); + ) + } + } + } + + } + + return KIp6Hook_PASS; // ensure we don't affect the control flow + } + + + +void CIPEventNotifier::ObtainEventService() + { + MInterfaceManager* ifacer = iProtocolIPv6->NetworkService()->Interfacer(); + + TRAPD(err, iEventService = IMPORT_API_L (ifacer, MEventService)); + if (err != KErrNone) + { + LOG ( + _LIT(KHookNotifyStr, "MEventService not available (%d). Some features may be missing\n"); + HookLog::Printf(KHookNotifyStr, err); + ) + } + } + + + + +void CIPEventNotifier::PublishMFlagL(TInt aIpIfIndex, TBool aMflag, TBool aOflag) + { + CIPEventNotifierInterface* iface = GetInterfaceWithIndexL(aIpIfIndex); + + if(iface) + { + if(iface->iMFlagReceived == 0) + { + iface->iMFlagReceived = CMFlagReceived::NewL(); + User::LeaveIfError(DefineProperty(*(iface->iMFlagReceived), aIpIfIndex)); + } + + iface->iMFlagReceived->SetMFlag(aMflag); + iface->iMFlagReceived->SetOFlag(aOflag); + PublishToHandle(aIpIfIndex, *(iface->iMFlagReceived)); + } + } + + +void CIPEventNotifier::PublishIPReadyL(TInt aIpIfIndex, const TInetAddr& tmpInetAddr, TBool addressValid) + { + CIPEventNotifierInterface* iface = GetInterfaceWithIndexL(aIpIfIndex); + + if(iface) + { + if(iface->iIPReady == 0) + { + iface->iIPReady = CIPReady::NewL(); + User::LeaveIfError(DefineProperty(*(iface->iIPReady), aIpIfIndex)); + } + + iface->iIPReady->SetIPAddress(tmpInetAddr); + iface->iIPReady->SetAddressValid(addressValid); + PublishToHandle(aIpIfIndex, *(iface->iIPReady)); + } + } + +void CIPEventNotifier::PublishLinklocalAddressKnownL(TInt aIpIfIndex, const TInetAddr& tmpInetAddr) + { + CIPEventNotifierInterface* iface = GetInterfaceWithIndexL(aIpIfIndex); + + if(iface) + { + if(iface->iLinklocalAddressKnown == 0) + { + iface->iLinklocalAddressKnown = CLinklocalAddressKnown::NewL(); + User::LeaveIfError(DefineProperty(*(iface->iLinklocalAddressKnown), aIpIfIndex)); + } + iface->iLinklocalAddressKnown->SetIPAddress(tmpInetAddr); + PublishToHandle(aIpIfIndex, *(iface->iLinklocalAddressKnown)); + } + } + + +void CIPEventNotifier::PublishLastKnownStates(CIPEventNotifierInterface* aIface) +{ + TUint32 ifIdx = aIface->GetInterfaceIndex(); + + // For each message from which we need state, not just events + // i.e. everything that might happen before DHCP starts and registers + // + if(aIface->iMFlagReceived) + { + PublishToHandle(ifIdx, *(aIface->iMFlagReceived)); + } + if(aIface->iLinklocalAddressKnown) + { + PublishToHandle(ifIdx, *(aIface->iLinklocalAddressKnown)); + } +}