diff -r 000000000000 -r af10295192d8 networkprotocols/tcpipv4v6prt/src/iface.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networkprotocols/tcpipv4v6prt/src/iface.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,12106 @@ +// 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: +// iface.cpp - IPv6/IPv4 interface and route manager +// Implementations of flows, routes and interfaces for IPv6. +// CIp6Manager +// |iInterfaceList +// / iFlowList\ / iRouteList\ | +// V iRoute \ V iInterface \V +// CIp6Flow ------------> CIp6Route -----------> CIp6Interface ---> NIF +// |iNext |iNext iAddress/|iNext +// | | CIp6Address | +// | | (list) | | +// V V | +// CIp6Flow CIp6Route V +// | | CIp6Interface +// íAddress/| +// CIp6Address +// A sketch of the Interface state transitions from StartSending/Error/Send calls +// and the resulting return values for StartSending (UP, READY). Not +// shown, but NONE is returned when StartSending is called in READY +// (unless address is changed, in which case UP is returned regardless +// of previous state). +// / Error( <0 ) / Error( <0 ) | +// | | V +// PENDING ------------> READY --------------> DOWN +// StartSending | ^ Error( <0 ) | +// ( == UP) | | / +// Send| |( == READY) / +// return| |StartSending / +// V | / +// HOLD ---------> +// Error (<0) +// Define WEAK_ES, if you don't want STRONG ES model for +// the host. +// + + + +/** + @file iface.cpp + @verbatim + @endverbatim + @verbatim + @endverbatim +*/ +#undef WEAK_ES +//#define WEAK_ES + +// Support for IPv6 DNS Configuration based on Router Advertisement + + +#define SYMBIAN_NETWORKING_UPS + +// +// In Epoc R6 nifman.h has been split, CNifIfBase definition has been moved +// into . +// +#include +#include +#include +#include // ..for CNifIfBase in Epoc R6 and later + +#include +#include +#include "inet6log.h" +#include "iface.h" +#include // IPv6 driver API specifications +#include +#include +#include +#include "in_flow.h" +#include +#include +#include "addr46.h" +#ifdef ARP +#include +#endif + +#include + +#ifdef SYMBIAN_NETWORKING_UPS +#include "in_trans.h" +#endif + +#include "tcpip_ini.h" +#include "networkinfo.h" +#include +#include + +#include "in6_version.h" +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#include +#endif + + +// Temporay backward portability definition, until +// KErrLinkConfigChanged is standard from some SDK +// version forward. For now, if a special version +// is installed, define LINK_CONFIG_CHANGED in MMP +// file. -- msa +#ifdef LINK_CONFIG_CHANGED +# include +#else +# define KErrLinkConfigChanged (-3060) +#endif + +#include +#include + +// +// DAEMON_USE_PROCESSES must be set in MMP file if required by SDK. It +// determines whether daemons are to be run on threads or on real +// processes. Only force here that processes are always used when +// compiling for target device. +// + +/** +* The basic timer unit. +* +* To enable compile time optimization, the unit is +* defined as preprocessor constant. The value indicates +* the fraction of the second to be used as a basic unit +* of the timer. This can be from 1 to 1000000 (from 1 +* second to 1 microsecond). +*/ +#define TIMER_UNIT 100 +#ifdef SYMBIAN_TCPIPDHCP_UPDATE +const TInt KRDNSSGranularity = 4; // Shall hold not more than 4 RDNSS Address +#endif //SYMBIAN_TCPIPDHCP_UPDATE + +// +//lint -save -e708 stupid lint info +/** +* @name Well known multicast and other addresses of the neighbour discovery. +* +* @{ +*/ +/** Multicast to all receivers on this node. */ +const TIp6Addr KInet6AddrNodeLocal = {{{0xff,0x01,0,0,0,0,0,0,0,0,0,0,0,0,0,1}}}; +/** Multicast to all hosts on the link. */ +const TIp6Addr KInet6AddrAllNodes = {{{0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,1}}}; +/** Multicast to all routers on the link */ +const TIp6Addr KInet6AddrAllRouters = {{{0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}}; +/** @} */ +//lint -restore + +// speed optimisations +#ifdef __ARMCC__ +#pragma push +#pragma arm +#endif + +class TInetNdConfig + /** + * Neighbor Discovery (RFC-2461) Protocol Constants. + * + * The constants are defined as members of TInetNdConfig, because + * in future it is possible that a link layer specific variations + * will be defined and some control option is provided to access + * them. + * + * (with some extras from RFC-2462). + */ + { +public: + // - router constants + TUint iMaxInitialRtrAdvertInterval; //< seconds + TUint iMaxInitialRtrAdvertisements; //< transmissions + TUint iMaxFinalRtrAdvertisements; //< transmissions + TUint iMinDelayBetweenRas; //< seconds + // - host constants + TUint iMaxRtrSolicitationDelay; //< seconds + TUint iRtrSolicitationInterval; //< seconds + TUint iMaxRouterSolicitations; //< transmissions + // - node constants + TUint iMaxMulticastSolicit; //< transmissions + TUint iMaxUnicastSolicit; //< transmissions + TUint iMaxAnycastDelayTime; //< seconds + TUint iMaxNeighborAdvertisement; //< transmissions + TUint iReachableTime; //< milliseconds + TUint iRetransTimer; //< milliseconds + TUint iDelayFirstProbeTime; //< seconds + TReal iMinRandomFactor; // + TReal iMaxRandomFactor; + + // RFC-2462 additions + TUint iDupAddrDetectTransmits; //< transmissions + + // IPv4 (and IPv6?) (draft-ietf-zeroconf-ipv4-linklocal-05) + TUint iMaxAddrRegenerations; //< addresses generated + TUint iDupAddrDefendTime; //< seconds + + // IPv4 Linklocal Address specifications (draft-ietf-zeroconf-ipv4-linklocal-05) + TUint iIPv4DupAddrDetectTransmits; //< transmissions + TUint iIPv4DupAddrAnnouncements; //< announcements + TUint iIPv4RetransTimer; //< seconds (for Dup and Announce) + + // Router Reachability probing (draft-ietf-ipv6-router-selection-02.txt) + TUint iRateLimitProbingTime; //< seconds + }; + +/** The current default values (from RFC-2461). */ +const TInetNdConfig KInetNdConfig = + { + //- router constants + /* MaxInitialRtrAdvertInterval */ 16, // seconds + /* MaxInitialRtrAdvertisements */ 3, // transmissions + /* MaxFinalRtrAdvertisements */ 3, // transmissions + /* MinDelayBetweenRas */ 3, // seconds + // - host constants + /* MaxRtrSolicitationDelay */ 1, // second + /* RtrSolicitationInterval */ 4, // seconds + /* MaxRouterSolicitations */ 3, // transmissions + // - node constants + /* MaxMulticastSolicit */ 3, // transmissions + /* MaxUnicastSolicit */ 3, // transmissions + /* MaxAnycastDelayTime */ 1, // second + /* MaxNeighborAdvertisement */ 3, // transmissions + /* ReachableTime */ 30000, // milliseconds + /* RetransTimer */ 1000, // milliseconds + /* DelayFirstProbeTime */ 5, // seconds + /* MinRandomFactor */ 0.5, + /* MaxRandomFactor */ 1.5, + + // - RFC-2462 additions + /* DupAddrDetectTransmits */ 1, // transmissions + + // IPv4 (and IPv6?) (draft-ietf-zeroconf-ipv4-linklocal-05) + /* MaxAddrRegenerations */ 10, // max addresses generated + /* DupAddrDefendTime */ 10, // seconds + + // IPv4 Linklocal Address specifications (draft-ietf-zeroconf-ipv4-linklocal-07) + /* IPv4DupAddrDetectTransmits */ 3, // transmissions + /* IPv4DupAddrAnnouncements */ 2, // announcements + /* IPv4RetransTimer */ 1, // seconds (for Dup and Announce) + + // Router Reachability probing (draft-ietf-ipv6-router-selection-02.txt) + /* iRateLimitProbingTime */ 60 // seconds + }; + + +/** +* @name Route Preference constants +* +* @{ +*/ +/** Route preference values. */ +enum TRoutePreference + { + ERoutePreference_MEDIUM = 0, //< Prf = 0 + ERoutePreference_HIGH = 1, //< Prf = 1 + ERoutePreference_INVALID= 2, //< Prf = -0 + ERoutePreference_LOW = 3 //< Prt = -1 + }; + +/** Translate TRoutePrefence value to route metric. */ +const TInt KPreferenceMetric[4] = + { + 1, // 0 (medium) and the default for metric in all created routes. + 0, // 1 (high) + 0, // 2 (invalid) + 2, // 3 (low) + }; +/** @} */ + +// +// TIcmpNdHeader +// ************* +class TIcmpNdHeader + /** + * Collection of the ICMP Messages relating to the + * Neigbor Discovery (RFC 2461). + */ + { +public: + // + // Basic + // + inline static TInt MinHeaderLength() {return 8; } + inline static TInt MaxHeaderLength() {return 40; } // Not much useful + + union + { + TInet6HeaderICMP iIcmp; + TInet6HeaderICMP_RouterSol iRS; + TInet6HeaderICMP_RouterAdv iRA; + TInet6HeaderICMP_NeighborSol iNS; + TInet6HeaderICMP_NeighborAdv iNA; + TInet6HeaderICMP_Redirect iRD; + }; + }; +// +// TIcmpNdOption +// ************* +class TIcmpNdOption + /** + * Collection of the ICMP options relating to the + * Neighbor Disovery (RFC 2461). + */ + { +public: + inline static TInt MinHeaderLength() {return 8; } + inline static TInt MaxHeaderLength() {return 40; } // Not much useful + + union + { + TInet6OptionICMP_LinkLayer iLink; + TInet6OptionICMP_Prefix iPrefix; + TInet6OptionICMP_Mtu iMtu; +#if 1 + // Experimental: draft-ietf-ipv6-router-selection-02.txt + // Default Router Preferences, More-Specific Routes, and Load Sharing + TInet6OptionICMP_RouteInformation iRouteInformation; +#endif +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + // IPv6 DNS Configuration based on Router Advertisement: RFC-5006 + TInet6OptionICMP_DnsInformationV1 iDnsInformation; +#else + // Experimental: draft-jeong-dnsop-ipv6-discovery-03.txt + // IPv6 DNS Configuration based on Router Advertisement + TInet6OptionICMP_DnsInformation iDnsInformation; +#endif //SYMBIAN_TCPIPDHCP_UPDATE + }; + }; + +// TRouteAddress +// ************* +class TRouteAddress + /** + * Internal class to hold an address. + * + * Internal help class which can hold any address of the TSockAddr, but + * does not inlude the port field or TBuf8 descriptor. Mainly required + * to get rid of the TSockAddr constructor, which prevents it's use + * inside union structure. + * + * If family is KAfInet6, then address is IPv6 or IPv4 address; otherwise + * the address is assumed to be a link layer address. + * + * Only a raw addresses are handled. Port, Scope Id and Flow label are + * not included. + */ + { +public: + inline TUint Family() const { return iFamily; } + inline TPtrC8 Address() const + /** + * Get the raw address bytes. + * @return descriptor for the raw address bytes. + */ + { + return TPtrC8(iBuf, iLength); + } + inline const TIp6Addr &Ip6Address() const + /** + * Get the raw IPv6 address. + * + * @return IPv6 address. + */ + { + return (TIp6Addr &)iBuf[0]; + } + void SetAddress(const TIp6Addr &aAddr); + void SetAddress(const TSockAddr &aAddr); + void GetAddress(TSockAddr &aAddr) const; + TBool Match(const TSockAddr& aAddr) const; +private: + TUint iFamily; //< Address family (0 = KAFUnspec) + TUint iLength; //< The length of the stored address + // ..and enough space for any possible address used in TSockAddr + TUint8 iBuf[KMaxSockAddrSize];//< Address bytes. + }; + +void TRouteAddress::SetAddress(const TIp6Addr &aAddr) + /** + * Set address from raw IPv6 address. + * + * @param aAddr The Address. + */ + { + ASSERT(sizeof(iBuf) >= sizeof(TIp6Addr)); + *(TIp6Addr *)iBuf = aAddr; + iFamily = KAfInet6; + iLength = sizeof(TIp6Addr); + } + +void TRouteAddress::SetAddress(const TSockAddr &aAddr) + /** + * Set address from a TSockAddr. + * + * @param aAddr The Address + */ + { + TPtr8 ptr(iBuf, sizeof(iBuf)); + ptr = TLinkAddr::Cast(aAddr).Address(); + + // Unfortunately, IPv6 addresses have to be treated specially, + // the iLength must reflect the plain IPv6 address, and not the + // TInetAddr user length. Otherwise, setting IPv6 + // from TInetAddr and TIp6Addr will not result matching + // entries... (icky! Perhaps needs some other fix..) + // Someone is bound to trip over this!! -- msa + // [...to use TInetAddr Userlen() is *NOT* a solution. Comparisons + // should only involve IPv6 address and not include scope/flow etc.] + iFamily = aAddr.Family(); + iLength = iFamily == KAfInet6 ? sizeof(TIp6Addr) : ptr.Length(); + } + +void TRouteAddress::GetAddress(TSockAddr &aAddr) const + /** + * Gets stored address into TSockAddr + * + * @retval aAddr The address. + */ + { + // Have to undo the trickery in SetAddress (yet another yechh!) -- msa + if (iFamily == KAfInet6) + TInetAddr::Cast(aAddr).SetAddress(Ip6Address()); + else + { + aAddr.SetFamily(iFamily); + TLinkAddr::Cast(aAddr).SetAddress(Address()); + } + } + +TBool TRouteAddress::Match(const TSockAddr& aAddr) const + /** + * Tests if the stored address matches another address. + * + * @param aAddr Another address. + * + * @return ETrue, if addresses are same; otherwise EFalse. + */ + { + if (iFamily != aAddr.Family()) + return FALSE; + if (iFamily == KAFUnspec) + return TRUE; + if (iFamily == KAfInet6) + return Ip6Address().IsEqual(TInetAddr::Cast(aAddr).Ip6Address()); + else + return Address() == TLinkAddr::Cast(aAddr).Address(); + } + + +// TSolicitedNodeAddr +// ******************* +class TSolicitedNodeAddr : public TIp6Addr + /** + * Generates Solicited Node Multicast address. + * + * An class whose sole purpose is to construct an intialized + * TIp6Address, which holds a solicited node multicast address + */ + { +public: + TSolicitedNodeAddr(const TIp6Addr &aAddress) + { + const union { TUint8 a[4]; TUint32 b; } mc_node = {{0xff, 0x02, 0, 0}}; + const union {TUint8 a[4]; TUint32 b;} one = { {0, 0, 0, 1} }; + const union {TUint8 a[4]; TUint32 b;} ff = { {0xff, 0, 0, 0} }; + + u.iAddr32[0] = mc_node.b; + u.iAddr32[1] = 0; + u.iAddr32[2] = one.b; + u.iAddr32[3] = ff.b | aAddress.u.iAddr32[3]; + } + }; + +// Lifetime definitions +// ******************** +// (values are seconds) +// +typedef TUint32 TLifetime; +const TUint32 KLifetimeForever = KMaxTUint32; + +// +// The implementations of +// CIp6Interface +// CIp6Route +// CIp6Flow +// CIp6NifUser +// CIp6Daemon +// are internal to this module and thus the class declaration do not need to +// be visible to any outsider. +// +// *NOTE* +// The public/private/protected and friend designations are total mess +// and should be cleaned up, if nothing else, then make all public, as +// these classes cross reference each other too much... -- msa +// +class CIp6Flow; +class CIp6Route; +class CIp6Interface; +class CIp6NifUser; +class CIp6Daemon; +class MNifIfUser; +class CNifIfBase; +class MTimeoutManager; + +// +// CIp6Manager +// *********** +// +class CIp6Manager : public CIfManager, public MNetworkInfo + , public MProvdSecurityChecker + { + // ... lots of "friends", look into this later -- msa + friend class CIp6Flow; + friend class CIp6Interface; + friend class CIp6Route; + friend class CIp6NifUser; + friend class CIp6ManagerTimeoutLinkage; + // + // Construct and InitL are only used from CIfManager::NewL() + // + friend class CIfManager; + + CIp6Manager(); + void InitL(); + // + virtual ~CIp6Manager(); + TBool LoadConfigurationFile(); +public: + // Access to the configuration file (tcpip.ini) + TBool FindVar(const TDesC &aSection,const TDesC &aVarName,TPtrC &aResult); + TBool FindVar(const TDesC &aSection,const TDesC &aVarName,TInt &aResult); + // + // Implement virtual methods required by the CIfManager + // + inline TInt FlowCount() { return iFlows; } + inline TInt UserCount() { return iUsers; } + inline TInt NifCount() { return iNifCount; } + + virtual void AddRouteL(const TIp6Addr &aAddr, TInt aPrefix, const TDesC &aName, + TUint aFlags = KRouteAdd_ONLINK, const TSockAddr *const aGateway = NULL, const TUint32 *const aLifetime = NULL); + virtual TInt CheckRoute(const TIp6Addr &aAddr, const TUint32 aScopeid, TIp6Addr &aSrc) const; + virtual TUint32 LocalScope(const TIp6Addr &aAddr, const TUint32 aLock, const TScopeType aLockType) const; + virtual TUint32 RemoteScope(const TIp6Addr &aAddr, const TUint32 aLock, const TScopeType aLockType) const; + virtual TUint32 IsForMeAddress(const TIp6Addr &aAddr, const TUint32 aInterfaceIndex) const; + virtual TInt IsForMePacket(RMBufRecvInfo &aInfo) const; + + virtual const MInterface* Interface(const CNifIfBase *const aIf) const; + virtual const MInterface* Interface(const TDesC &aName) const; + virtual const MInterface* Interface(const TUint32 aInterfaceIndex) const; + + // Get* methods are new versions of InterfaceInfo and RouteInfo. + // Instead of iterating through by returning one + // entry per each call, Get* methods return an array of entries in aOption buffer when + // returning. I.e., Get* methods return an atomic snapshot of the current status. + virtual TUint InterfaceInfo(TUint aIndex, TSoInetInterfaceInfo &aInfo) const; + virtual TUint RouteInfo(TUint aIndex, TSoInetRouteInfo &aInfo) const; + + // Doxy descriptions for the Get*() methods can be found in MNetworkInfo definition. + // These implement MNetworkInfo, thus Doxygen shows the same comments here + virtual TInt GetInterfaces(TDes8& aOption) const; + virtual TInt GetAddresses(TDes8& aOption) const; + virtual TInt GetRoutes(TDes8& aOption) const; + + // Options processing + virtual TInt GetOption(TUint aLevel, TUint aName, TDes8 &aOption) const; + virtual TInt SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption); + TInt CheckPolicy(const TSecurityPolicy& /*aPolicy*/, const char */*aDiagnostic*/) { return KErrNone; } + virtual TInt GetOption(TUint aLevel, TUint aName, TDes8 &aOption, MProvdSecurityChecker &aChecker) const; + virtual TInt SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption, MProvdSecurityChecker &aChecker); + + // "Users" housekeeping. + virtual void IncUsers(); + virtual void DecUsers(); + // + virtual TInt PacketAccepted(const TUint32 aInterfaceIndex); + + // Flows + virtual CFlowContext *NewFlowL(const void *aOwner, MFlowManager *aManager, TUint aProtocol); + virtual CFlowContext *NewFlowL(const void *aOwner, MFlowManager *aManager, CFlowContext &aFlow); + virtual TInt SetChanged() const; + // + // + virtual TInt StartSending(CNifIfBase *aIface); + virtual TInt Error(TInt aError, CNifIfBase *aIface); + // + // Protocol registering (iNifUsers list) + // + virtual MNifIfUser *Register(MNetworkServiceExtension *aProtocol); // Makes protocol visible to interfaces + virtual void Unregister(MNetworkServiceExtension *aProtocol); // Removes protocol (called from protocol destructor) + // ICMP stuff + TInt IcmpError(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo); + TInt IcmpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo); + +#ifdef ARP + virtual TInt ArpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo); +#endif + // + // Accessing the main components uniformly independent of the class + // (when linkages between classes change, just change these to reflect + // the change, and the rest of the code should work unchanged) + // + inline CIp6Manager &Interfacer() { return *this; }; + + TInt GetDstCachePathMtu(const TIp6Addr& aDstAddress, TUint32 aScopeId) const; + void *GetApiL(const TDesC8& aApiName, TUint* aVersion); + + // Returns the event manager instance used by the stack + inline MEventService *EventManager() const { return iEventManager; } + + // Wrap a packet into ICMP error reply + void IcmpSend(RMBufChain &aPacket, const TIcmpTypeCode aIcmp, const TUint32 aParameter = 0, const TInt aMC = 0); + +private: +# ifdef WEAK_ES + TUint32 IsForMe(const TIp6Addr &aAddr, const CIp6Interface *const aSrcIf, + const TUint32 aScopeId, const TScopeType aType) const; +# else + TUint32 IsForMe(const TIp6Addr &aAddr, const CIp6Interface *const aSrcIf) const; +# endif + // + // + CIp6Interface *FindInterface(const CNifIfBase *aInterface) const; + CIp6Interface *FindInterface(const TAny *aId) const; + CIp6Interface *FindInterface(const TInetAddr &aAddr) const; + CIp6Interface *FindInterface(const TUint32 aIndex) const; + CIp6Interface *FindInterface(const TDesC &aName) const; + CIp6Interface *FindInterface(const TUint32 aIndex, const TScopeType aLevel) const; + // + // Internal Route manipulation + // + CIp6Route *FindRoute + (const TIp6Addr &aDst, const TUint32 aDstId, const TUint aDstType, + const TIp6Addr &aSrc = KInet6AddrNone, const TUint32 aSrcId = 0) const; + void ProbeDestination + (const TIp6Addr &aDst, const TUint32 aDstId, const TUint aDstType, + const TIp6Addr &aSrc = KInet6AddrNone, const TUint32 aSrcId = 0) const; + // + // "HoldingRoute()" returns the dummy "default" route entry that gets all + // the flows that wait for interface setup (like dialup). + // + CIp6Route *HoldingRoute() const { return iHoldingRoute; } + // + // Moving flow()s to holding route + // + void MoveToHolding(CIp6Flow &aFlow) const; // Move the specific aFlow + void MoveToHolding(CIp6Route &aRoute) const; // Move all flows from aRoute + + // + // ScanHoldings() scans the flows in the special holding route and + // checks if any of them could now be assigned to a real route (and + // does so, if yes). + void ScanHoldings(); + // + // Get interface by name (and create a new entry, if not found) + // + CIp6Interface *GetInterfaceByNameL(const TDesC &aName); + // + // Unconditional removal of the interface + // + void RemoveInterface(CIp6Interface *aIf); + // + // Modify Inet Interface information (SetOption part!) + // + TInt InetInterfaceOption(TUint aName, const TSoInet6InterfaceInfo &aInfo); + // + // Query Interface Information + // + TInt InterfaceQueryOption(TUint aName, TSoInetIfQuery &aQuery, const TInt aLength) const; + // + // A gateway from Set/Get Option to interface + // + TInt InterfaceOption(TUint aLevel, TUint aName, TDes8 &aOption) const; + + // Called when SetOption for KSoIpv4LinkLocal has been issued. + TInt SetIpv4LinkLocalOption(const TSoInetIpv4LinkLocalInfo &aOption); + + // + // Multicast Join/Leave Group processing + // + TInt MulticastOption(TUint aName, const TIp6Mreq &aRequest); + // + // Automatic Daemon control (start/stop) + // + void StartDaemons(); + void StopDaemons(); + void Timeout(const TTime &aStamp); // Timer expiration event handler + static TUint TimerUnits(const TUint aDelay, const TUint aUnit = 1); + void SetTimer(RTimeout &aHandle, TUint32 aDelay); + + inline void SetTimerWithUnits(RTimeout &aHandle, TUint32 aDelay) + { + iTimeoutManager->Set(aHandle, aDelay); + } + + // + // Set/reset a timer event on the current object + // + inline void SetTimer(TUint32 aDelay) { SetTimer(iTimeout, aDelay); } + // CancelTimer/IsTimerActive are just syntactic sugar, because + // of the SetTimer: if one uses SetTimer and hides iTimeout, then + // all uses of iTimeout should be "hidden" too! + inline void CancelTimer() { iTimeout.Cancel(); } + inline TBool IsTimerActive() { return iTimeout.IsActive(); } + + // + // Get tcpip.ini values + // + TInt GetIniValue(const TDesC &aSection, const TDesC &aName, TInt aDefault = 0, TInt aMin = 0, TInt aMax = 1); + + CIp6Route *iHoldingRoute; //< Always Exists! The place for pending flows + CIp6Interface *iInterfaceList; //< All interfaces + CIp6Daemon *iDaemons; //< Daemons created in InitL + TInt iLinkLocalTTL; //< Default TTL/Hoplimit for unicast link local destinations + TUint8 iMaxTTL; //< Default TTL/Hoplimit + TUint8 iRA_OptRoute; //< Assigned value for KInet6OptionICMP_RouteInformation (until fixed by IANA) +#ifndef SYMBIAN_TCPIPDHCP_UPDATE + TUint8 iRA_OptDns; //< Assigned value for KInet6OptionICMP_DnsInformation (until fixed by IANA) +#endif //SYMBIAN_TCPIPDHCP_UPDATE + // Default value for the flow iNoInterfaceError flag + TUint iNoInterfaceError:1; + // Default value for the flow iKeepInterfaceUp flag + TUint iKeepInterfaceUp:1; + + // Configure IPv4 link local addresses, if non-zero. + // Determines the default interface-specific setting in CIp6Interface + // (see there for usable values). + TUint iIpv4Linklocal:3; + + // Disable "ID defense mode", if non-zero + TUint iNoDefendId:1; + // Enable ND probing for addresses for which there is no route + TUint iProbeAddress:1; + + // = 1, if holding queue should be scanned + TUint iScanHolding:1; + /** + // Number of seconds to wait, before killing the daemons (DND etc) + // after the last *counted* user (SAP, NIF) exits. + */ + TUint iShutdownDelay; + // A maximum timelimit for holding flows (seconds). + TUint iMaxHoldingTime; + /** + // iMaxTickInterval is precomputed at initialize and holds the + // longest time interval in seconds that can be expressed + // with tick counts (when used as time stamps and compared) + // (= number of seconds corresponding KMaxTInt ticks) + */ + TUint iMaxTickInterval; + // for Random sequence... + TInt64 iSeed; + // + // iNifUser array is filled at init with + // allocated objects of CIp6NifUser. A Register() + // call from a protocol will fill in itself to + // the appropriate slot (overriding any previous + // register). + // + // All this because I don't know for sure what NIF wants, + // but it does appear to assume that there is one-to-one + // mapping between a protocol instance and MIfNifUser. + // -- msa + enum + { + E_IPv4 = 0, // for a protocol supporting KAfInet + E_IPv6 = 1, // for a protocol supporting KAfInet6 + E_IPmax + }; + CIp6NifUser *iNifUser[E_IPmax]; + + TInt iUsers; //< Count of active users + TInt iNifCount; //< Count of NIF references + TInt iFlows; //< Count of Flow contexts + // + TUint iInterfaceIndex; //< Last assigned interface index (or zero) + TUint iRouteIndex; //< Last assigned route index (or zero) + MTimeoutManager *iTimeoutManager; //< Provide Timer Services for the Interface Manager + CESockIniData *iConfig; //< Configuration data + TInt iConfigErr; //< Non-zero, if configuration file is not available + + MEventService *iEventManager; //< For providing interface and route events to the plugins. + MDestinationCache *iDestinationCache; //< Destination cache (for transport protocol params). + +public: // GCC doesn't compile Linkage, if this is private! -- msa + RTimeout iTimeout; //< Hook to the timer service (MTimeoutManager) + }; + +// +// CIp6ManagerTimeoutLinkage +// ************************* +// *NOTE* +// This kludgery is all static and compile time, and only used in the constructor +// of CIp6Interface. +// + +// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be +// passed as a template parameter +#if defined(__X86GCC__) || defined(__GCCE__) +#define KIp6ManagerTimeoutOffset 104 +__ASSERT_COMPILE(KIp6ManagerTimeoutOffset == _FOFF(CIp6Manager, iTimeout)); +#else +#define KIp6ManagerTimeoutOffset _FOFF(CIp6Manager, iTimeout) +#endif + +class CIp6ManagerTimeoutLinkage : public TimeoutLinkage + /** + * Glue to bind timeout callback from the timeout manager into Timeout() call + * on the CIp6Route + */ + { +public: + static void Timeout(RTimeout &aLink, const TTime &aNow, TAny * /*aPtr*/) + { + LOG(Log::Printf(_L("<>\tCIp6Manager Timeout"))); + Object(aLink)->Timeout(aNow); + } + }; + +// +// CIp6NifUser +// *********** +// +#ifdef _LOG +_LIT(KIPv4, "IPv4"); +_LIT(KIPv6, "IPv6"); +#endif + +class CIp6NifUser : public CBase, public MNifIfUser + { + friend class CIp6Manager; + friend class CIp6Interface; + friend class CIp6Flow; + friend class CIp6Route; + CIp6NifUser(CIp6Manager &aManager, TBool aIPv4) : iManager(aManager), iIPv4(aIPv4) {} + inline TBool IsIPv4() const {return iIPv4; } +#ifdef _LOG + const TDesC &LogName() const { return IsIPv4() ? KIPv4() : KIPv6(); } +#endif +public: + // + // Interface interface + // + void IfUserBindFailure(TInt aResult, TAny* aId); + void IfUserNewInterfaceL(CNifIfBase* aIf, TAny* aId); + void IfUserInterfaceDown(TInt aResult, CNifIfBase* aIf); + void IfUserOpenNetworkLayer(); + void IfUserCloseNetworkLayer(); + CProtocolBase* IfUserProtocol(); + TBool IfUserIsNetworkLayerActive(); + TBool IfUserIsNetworkLayerActive(CNifIfBase *); + // + // Accessing the main components uniformly independent of the class + // (when linkages between classes change, just change these to reflect + // the change, and the rest of the code should work unchanged) + // + inline CIp6Manager &Interfacer() const { return iManager; }; +private: + CIp6Manager &iManager; + const TBool iIPv4; // True for IPv4, False otherwise + MNetworkServiceExtension *iNetwork; + }; + + +// *************** +// TIp6AddressInfo +// *************** +// +class CIp6Address; +class TIp6AddressInfo + { +public: + // Match returns TRUE, if aAddr matches the ID/hostnumber. + TBool Match(const TIp6Addr &aAddr) const; + + // Match returns TRUE if aAddr ID matches exactly (prefix is + // not used to mask address bits). + TBool MatchExactly(const TIp6Addr &aAddr) const; + + CIp6Address *iNext; + /** + // iId and iPrefix define the ID/hostnumber portion of + // the address, aligned to end of the iId field. + // + // iPrefix is *usually* the length of the prefix to be + // used with this id. Technically, iPrefix is the number + // of bits in the iId, that DO NOT BELONG to the stored id. + // The legal values are [0..128]. *Note* Storing address + // with iPrefix=128 will make that id part match any + // address (id length == 0!) -- careful with it! + // + // This is designed for IPv6 addresses, but the processing + // is "tweaked" so that the same code works also for IPv4 + // as follows: + // + // @li IPv4 loopback net (127.x.x.x) is coded as + // route = ELoopback, 127.0.0.0/8, address = ::/128 + // => Address match depends only on ELoopback prefix + // + // @li IPv4 address + // route = ELoopback, ::ffff:ipv4/128, + // address = ::ffff:ipv4/0. IPv4 is treated as single + // unit (not split into prefix and id) + // + // @li IPv6 loopback is coded + // route = ELoopback, ::1/128, address= ::/128 + // => Address match depends only on ELoopback prefix + */ + TIp6Addr iId; //< The Id value (aligned to the end) + TUint8 iPrefix; //< Number of bits to skip before id. + // + // Duplicate Address Detection + // + TUint8 iNS; //< Number of NS sent for DAD + /** + // Generated address counter. If value is non-zero, + // then this address has been randomly generated and + // on duplicate address collision, it is legal to + // regenerate another address. iGenerated counts + // the number of address generations. + */ + TUint8 iGenerated; //< Number of address generations + // + // Address type + // + enum TAddressType + { + EProxy = 2, //< Do DAD, is not for me (forward) + EAnycast = 1, //< Don't do DAD, is for me address + ENormal = 0, //< Do DAD, is for me + }; +private: + TUint iType:2; + TBool iPrimary; + // + // Address state + // + enum TState + { + ENoAddress = 0, //< 0 0 - unassigned initial state (no address present) + EDuplicate = 1, //< 0 1 - address is duplicate + EAssigned = 2, //< 1 0 - address fully available + ETentative = 3 //< 1 1 - address is tentative (DAD in progress) + }; + TUint iState:2; +public: + /** + // A flag to mark an internally generated IPv4 link-local address. + // + // There can only be at most one of these per interface. + */ + TUint iIpv4LinkLocal:1; + // + // + inline TInt AddressType() const { return (TInt)iType; } + inline TInt AddressState() const { return (TInt)iState; } + inline TBool IsSet() const { return iState != ENoAddress; }; + inline TBool IsTentative() const { return iState == ETentative; } + inline TBool IsAssigned() const { return iState == EAssigned; } + inline TBool IsDuplicate() const { return iState == EDuplicate; } + inline TBool IsAnycast() const { return iType == EAnycast; } + inline TBool IsProxy() const { return iType == EProxy; } + inline TBool IsNormal() const { return iType == ENormal; } + inline TBool IsPrimary() const { return iPrimary; } + inline void SetInitial(const TInt aTentative) { iState = aTentative ? ETentative : EAssigned; } + inline void SetDuplicate() { iState = EDuplicate; } + inline void SetNoAddress() { iState = ENoAddress; } + inline void SetType(const TInt aType) + { + iType = (TUint)aType; + // ..anycast address is always assigned (NO DAD performed) + if (aType == EAnycast) iState = EAssigned; + } + inline void SetPrimary(const TBool aPrimary ) { iPrimary = aPrimary; } +#ifdef _LOG + const TDesC &LogAddressType() const; + const TDesC &LogAddressState() const; +#endif + // + // Address Lifetimes (mainly for temporary address management) + // (privacy extension for IPv6, RFC 3041) + // + TTime iCreated; //< Creation Time (real time) + TLifetime iVLT; //< Valid lifetime (relative to iCRT) + TLifetime iPLT; //< Preferred lifetime (relative to iCRT) + }; + +#ifdef _LOG +const TDesC &TIp6AddressInfo::LogAddressType() const + { + _LIT(KProxy, "proxy "); + _LIT(KAnycast, "anycast "); + _LIT(KNormal, ""); + _LIT(KNormalPrimary, "primary "); + _LIT(KInvalid, "invalid "); + switch (iType) + { + case EProxy: return KProxy; + case EAnycast: return KAnycast; + case ENormal: + { + if( IsPrimary() ) + { + return KNormalPrimary; + } + else + { + return KNormal; + } + } + default: break; + } + return KInvalid; + } + +const TDesC &TIp6AddressInfo::LogAddressState() const + { + _LIT(KNoAddress, "none"); + _LIT(KDuplicate, "duplicate"); + _LIT(KAssigned, "assigned"); + _LIT(KTentative, "tentative"); + switch (iState) + { + case EDuplicate: return KDuplicate; + case EAssigned: return KAssigned; + case ETentative: return KTentative; + default: break; + } + return KNoAddress; + } + +#endif +#ifdef SYMBIAN_TCPIPDHCP_UPDATE +//RFC 5006 Changes +// +// CManageRdnssServerList::NewL() +// **************************** +// First Phase construction +CManageRdnssServerList* CManageRdnssServerList::NewL() + { + // Construct instance of type CMangeRdnssServerList + CManageRdnssServerList* me = new (ELeave)CManageRdnssServerList(); + CleanupStack::PushL(me); + me->ConstructL(); + CleanupStack::Pop(); + return me; + } + + +// CManageRdnssServerList::ConstructL +// **************************** +// +void CManageRdnssServerList::ConstructL() + { + + } + + +// CManageRdnssServerList::CManageRdnssServerList +// **************************** +// Sets Creation time for RDNSS entry +CManageRdnssServerList::CManageRdnssServerList():iRdnssArrayList(KRDNSSGranularity,_FOFF(TRdnssOptionData,iRDNSSaddress)),iRdnssLifetimeArrayList(KRDNSSGranularity) // set the granularity size to 4 + { + //Construct iRdnssArrayList of size kRDNSSGranularity=4 + //Set the initial current time stamp + + iCurrentTimeStamp.UniversalTime(); + } + + +// CManageRdnssServerList::~CManageRdnssServerList +// **************************** +// Destructor to clean up iRdnssArrayList +CManageRdnssServerList::~CManageRdnssServerList() + { + iRdnssArrayList.Close(); + iRdnssLifetimeArrayList.Close(); + } + + +// CManageRdnssServerList::InsertRdnssEntryL +// **************************** +// Inserts RDNSS entry into RDNSS server list +// Array shall hold 4 DNS entries, +// Returns ETrue if successful, if more than 4 received returns EFalse. +TBool CManageRdnssServerList::InsertRdnssEntryL(TRdnssOptionData& aRdnssEntry, TInt aIndex) + { + TInt numRdnssEntry = CountRdnssEntry(); + if( numRdnssEntry < KRDNSSGranularity )// Shall hold only 4 entries + { + iRdnssArrayList.InsertL(aRdnssEntry, aIndex); + return ETrue; + } + return EFalse; + } + + +// CManageRdnssServerList::GetRdnssEntryRef +// **************************** +// Gets a reference to RDNSS entry +TRdnssOptionData& CManageRdnssServerList::GetRdnssEntryRef(TInt aIndex) + { + return iRdnssArrayList[aIndex]; + } + + +// CManageRdnssServerList::DeleteRdnssEntry +// **************************** +// Deletes RDNSS Entry for corresponding index +void CManageRdnssServerList::DeleteRdnssEntry(TInt aRdnssArrayIndex) + { + // Removes entry from the array iRdnssServerList + iRdnssArrayList.Remove(aRdnssArrayIndex); + } + + +// CManageRdnssServerList::SetStoredLifeTime +// **************************** +// Sets StoredLifetime for a RDNSS Entry +void CManageRdnssServerList::SetStoredLifeTime(TInt aIndex, TRdnssOptionData aRdnssData) + { + // Sets field Lifetime at the aIndex + iRdnssArrayList[aIndex].iStoredRdnssLifeTime = aRdnssData.iStoredRdnssLifeTime; + } + + +// CManageRdnssServerList::Elapsed +// **************************** +// Returns the elapsed time with respect to created timestamp of RDNSS Entry +TLifetime CManageRdnssServerList:: Elapsed(const TTime &aStamp)const + { + TTimeIntervalSeconds elapsed; + aStamp.SecondsFrom(iCurrentTimeStamp, elapsed); + // Return 0, if time is earlier than time stamp (clock turned back?) + return (TLifetime) (elapsed.Int() < 0 ? 0 : elapsed.Int()); + } + + +// CManageRdnssServerList::PrintRdnssServerList +// **************************** +// Prints RDNSS Entries +void CManageRdnssServerList::PrintRdnssServerList(TUint8 aIndex) + { + TRdnssOptionData rdnssEntry = GetRdnssEntryRef(aIndex); + TBuf<70> tmpsrc; + TInetAddr inetAddr(rdnssEntry.iRDNSSaddress); + TInetAddr::Cast(inetAddr).OutputWithScope(tmpsrc); + LOG(Log::Printf(_L("\t [Index:%d],[RDNSS =%S],[Lifetime=%d]"),aIndex, &tmpsrc,rdnssEntry.iStoredRdnssLifeTime)); + } + + +// CManageRdnssServerList::GetRdnssFlag +// **************************** +// Returns the RDNSS repository Flag +TInt& CManageRdnssServerList::GetRdnssFlag() + { + // iRdnsFlag shall be updated corresponding to each NameServer Entry. + // iRdnsFlag shall be 0x01 for first NameServer entry. + // iRdnsFlag shall be 0x03 for first and second NameServer entry. + // iRdnsFlag shall be 0x04 when Namserver entries are reset to KAFUnspec. + + return iRdnssFlag; + } + + +// CManageRdnssServerList::GetRemainingLifeTime +// **************************** +// Returns remaining life time for existing 'rdnssEntry' entry +TLifetime CManageRdnssServerList::GetRemainingLifeTime(TInt aRdnssEntryindex) + { + TRdnssOptionData rdnssEntry = GetRdnssEntryRef(aRdnssEntryindex); + TRdnssLifetime elapsedLifetime = ElapsedLifeTime(rdnssEntry); +#ifdef _DEBUG + TBuf<70> tmpsrc; + TInetAddr inetAddr(rdnssEntry.iRDNSSaddress); + TInetAddr::Cast(inetAddr).OutputWithScope(tmpsrc); + LOG(Log::Printf(_L("\t [[RDNSS =%S]][Elapsed Lifetime=%d,]"),&tmpsrc,elapsedLifetime)); +#endif + return elapsedLifetime; + } + + +// CManageRdnssServerList::ElapsedLifeTime +// **************************** +// Compute the remaining life time for existing 'i' entry +TLifetime CManageRdnssServerList::ElapsedLifeTime(TRdnssOptionData aRdnssEntry) + { + // If iStoredRdnssLifetime is greater than current time, ie. entry is not expired, returns difference of + // (iStoredRdnssLifetime - curret_time). + // If current_time is greater than iStoredRdnssLifetime, then entry is expired, returns 0. + + TTime stamp; + stamp.UniversalTime(); + const TLifetime current_time = Elapsed(stamp); + + TLifetime elapsedLifetime = (aRdnssEntry.iStoredRdnssLifeTime > current_time)? + (aRdnssEntry.iStoredRdnssLifeTime - current_time):0; + return elapsedLifetime; + } + + +// CManageRdnssServerList::RdnssEntryExists +// **************************** +// Verify RDNSS Entry Exists in RDNSS Server List +TBool CManageRdnssServerList::RdnssEntryExists(const TIp6Addr& aAddress, TInt& aArrIndex ) + { + // Returns ETrue, with (aArrIndex)index of the (aAddress)RDNSS adress matched in iRdnssArrayList. + // Returns EFalse, if there exists no match of (aAddress)RDNSS address in iRdnssArrayList. + + TRdnssOptionData rdnssEntry; + TInt numRdnssEntry = CountRdnssEntry(); + for(TInt rdnssIndex=0;rdnssIndexb.iStoredRdnssLifeTime ? 1:-1; + } + else + return 0; + } + + +// CManageRdnssServerList::RdnssServerListSort +// **************************** +// Sorts on a temporary copied RDNSS Server List i.e iRdnssLifetimeArrayList on iStoredRdnssLifeTime Entry +void CManageRdnssServerList::RdnssServerListSort() + { + TLinearOrder order(RdnssOrderFunc); + iRdnssLifetimeArrayList.Sort(order); + } + + +// CManageRdnssServerList::RdnssServerListCopyL +// **************************** +// Copies contents from iRdnssArrayList to iRdnssLifetimeArrayList +void CManageRdnssServerList::RdnssServerListCopyL() + { + // Since sort on iRdnssArrayList alters the position related to NameServer entries, + // hence we copy the lifetime values of iRdnssArrayList to iRdnssLifetimeArrayList + // and then perform sort to determine the least lifetime entry. + + TInt rdnssCount = CountRdnssEntry(); // Get Number of Elements in iRdnssArrayList + TRdnssOptionData rdnssEntry; + TRdnssSortData lifetimeArrayList; + + for(TInt rdnssIndex=0;rdnssIndex tmpsrc; + TInetAddr inetAddr(rdnssEntry.iRDNSSaddress); + TInetAddr::Cast(inetAddr).OutputWithScope(tmpsrc); + LOG(Log::Printf(_L("\t [[RDNSS =%S]][Elapsed Lifetime=%d,]"),&tmpsrc,elapsedLifetime)); +#endif + // RDNSS entry is expired since its not updated by RA. + if(elapsedLifetime==0) + { + // Verify whether expired entry is a preferred entry matching index 0 or index 1 corresponding to iNameSer1/iNameSer2, + // If so reset the dns_flag. + if((rdnssIndex == 0) || (rdnssIndex == 1)) + { + RdnssNameServerSync(rdnssIndex,aNameSer1,aNameSer2); + } + else + { + //Expired Entry is not a Preferred Entry, remove the corresponding entry from iRdnssArrayList[i]. + DeleteRdnssEntry(rdnssIndex); + LOG(Log::Printf(_L("\t ...RDNSS Entry Deleted[Index=%d,]"),rdnssIndex)); + } + } + // RDNSS entry is about to expire, need to refresh RDNSS entry by initiating a RS + // Since CIP6Interface::Timeout handler expires for every 30 seconds. + if(elapsedLifetime<=RDNSS_REFRESH_TIMEOUT) + { + return sendRdnssRS = ETrue; + } + } //End of for() + return sendRdnssRS; + } + + +// CManageRdnssServerList::RdnssNameServerUpdate +// **************************** +// Update NameServer Repository iNameSer1/iNameSer2 in CIp6Interface from RDNSS server list +void CManageRdnssServerList::RdnssNameServerUpdate(TInetAddr& aNameSer, TUint8 aNameServerIndex) + { + // If RdnssArrayList exist, just update aNameSer(iNameSer1/iNameSer2 entries in CIp6Interface) with appropriate dns address. + // If RDNSSArrayList doesn't exist, Need to reset aNameSer(iNameSer1/iNameSer2 in CIp6Interface) to KAFUnspec. + + if( CountRdnssEntry()>aNameServerIndex ) + { + TRdnssOptionData rdnssEntry = GetRdnssEntryRef(aNameServerIndex); + + // aNameSerIndex for iNameSer1 ==> 1. + // aNameServerIndex for iNameSer2 ==> 2. + + // If NameServer entry doesnt exist, configure iNameSer1/iNameSer2 and mark "dns_changed" respect to repository being set.. + if( (iRdnssFlag&(aNameServerIndex+1))==0) + { + aNameSer.SetAddress(rdnssEntry.iRDNSSaddress.Ip6Address()); + iRdnssFlag |= (aNameServerIndex + 1); + } + // If Nameserver entry exists, needs to be refreshed due to fresh update of iRdnssArrayList. + else + { + aNameSer.SetAddress(rdnssEntry.iRDNSSaddress.Ip6Address()); + } + LOG(Log::Printf(_L("\t Updating NameServer Repository: NameSerIndex=%d, RdnssFlag=%d Lifetime=%d"),aNameServerIndex,iRdnssFlag,rdnssEntry.iStoredRdnssLifeTime)); + } + else + { + // Dont reset iRdnsFlag, since there is a single dns address yet to be expired. + // RdnssNameServerSync() shall delete repository entry, provided elapsed time is 0 and also shall reset iRdnssFlag appropriately. + LOG(Log::Printf(_L("\t Reset NameServer Repository: NameSerIndex=%d"),aNameServerIndex)); + aNameSer.Init(KAFUnspec); + } + } + + +// CManageRdnssServerList::RdnssNameServerReset +// **************************** +// Reset Rdnss Namserver Repository Upon expiry of lifetime to KAFUnspec. +void CManageRdnssServerList::RdnssNameServerReset(TInetAddr& aNameSer, TInt& aDdnsflag ) + { + aNameSer.Init(KAFUnspec); + // mark "dns changed" respect to repository being deleted. + aDdnsflag = aDdnsflag & 4; + } + + +// CManageRdnssServerList::RdnssNameServerSync +// **************************** +// Syncrhonise NameServer Repository entries in RDNSS server list. +void CManageRdnssServerList::RdnssNameServerSync(TInt aRdnssIndex, TInetAddr& aNameSer1, TInetAddr& aNameSer2) + { + // Since NameServer Entry is expired, remove the corresponding entry from iRdnssArrayList. + // Reset iNameSer1/iNameSer2 to KAFUnsepc + + TInetAddr& nameSer = aRdnssIndex==0?aNameSer1:aNameSer2; + RdnssNameServerReset(nameSer,GetRdnssFlag()); + DeleteRdnssEntry(aRdnssIndex); + LOG(Log::Printf(_L("\t ...RDNSS Repository Entry Deleted[Index=%d,]"),aRdnssIndex)); + } + + +// CManageRdnssServerList::RdnssServerListUpdate +// **************************** +// Append RDNSS Entry in RDNSS Server List or update existing entry. +void CManageRdnssServerList::RdnssServerListUpdate(TInet6OptionICMP_DnsInformationV1 aRdnssIcmpOption, TUint8 aNumRdnssAddr) + { + // Find Whether Received Entry Exists + // If found existing Entry, update stored lifetime. + // Else create a new Entry. + // If no room to accomodate a new Entry, find Worse Entry and delete. + // Upon successful deletion,Store new Entry. + + TRdnssOptionData rdnssRcvData; + TInt rdnssOptionPktSize; + + for(TUint8 rdnssAddrCount=0; rdnssAddrCount< aNumRdnssAddr;++rdnssAddrCount) + { + //Fetch the first RDNSS address from RDNSS option + rdnssOptionPktSize = aRdnssIcmpOption.HeaderLength()+((rdnssAddrCount+1)*RDNSSADDRSIZE); + + const TIp6Addr &addr = aRdnssIcmpOption.GetNextAddress(rdnssOptionPktSize-RDNSSADDRSIZE); + const TRdnssLifetime lifetime = aRdnssIcmpOption.Lifetime(); + rdnssRcvData.iRDNSSaddress.SetAddress(addr); + + TInt rdnssArrayIndex; + + //Find this entry exist in RDNSS Server List + if(!RdnssEntryExists(addr, rdnssArrayIndex)) + { + //Entry doesnt exist,Try to Insert it infront of the iRdnssArrayList + //Before updating the lifetime, convert into seconds to determine it is expired. + TTime stamp; + stamp.UniversalTime(); + const TRdnssLifetime current_time = Elapsed(stamp); + rdnssRcvData.iStoredRdnssLifeTime = lifetime + current_time ; // (It should be current system time + lifetime ...in seconds ) + LOG(Log::Printf(_L("\t Received Entry with Lifetime: %d"),rdnssRcvData.iStoredRdnssLifeTime)); + + if(!InsertRdnssEntryL(rdnssRcvData, 0)) + { + LOG(Log::Printf(_L("\t Insert Unsuccessful,Received More than 4 ENTRIES"))); + //If insert is unsuccessful, then try to find room for new entry. + RdnssExpireLeastEntry(rdnssRcvData); + } + } + else // Found an entry, need to update existing entry with suitable lifetime + { + // Delete an entry , if lifetime is zero. + if( lifetime==0 ) + { + // Entry is expired, remove the corresponding entry from iRdnssArrayList[i]. + DeleteRdnssEntry(rdnssArrayIndex); + continue; + } + else + //Update the entry 'i' with received lifetime, if remaininglifetime is greater than 0. + { + TTime stamp; + stamp.UniversalTime(); + const TRdnssLifetime current_time = Elapsed(stamp); + // (It should be current system time + lifetime ...in seconds ) + rdnssRcvData.iStoredRdnssLifeTime = lifetime + current_time; + SetStoredLifeTime(rdnssArrayIndex,rdnssRcvData); + } + } + }//end of For + + } + + +// CManageRdnssServerList::RdnssProcessOptionData +// **************************** +// Process received RDNSS Option Data from Router Adevertisement. +void CManageRdnssServerList::RdnssProcessOptionData(TInet6OptionICMP_DnsInformationV1 aRdnssOption, TUint8 aNumRdnssAddr ) + { + RdnssServerListUpdate(aRdnssOption,aNumRdnssAddr); +#ifdef _DEBUG + LOG(Log::Printf(_L("\tIF RDNSS TABLE PRINTED"))); + for(TUint8 index =0;index RDNSSMINLEN, else returns False. +TBool CManageRdnssServerList::RdnssParseOptionHdr(TInet6OptionICMP_DnsInformationV1 aRdnssOption, TUint8& aNumRdnssAddr ) + { + const TUint8 length = aRdnssOption.Length(); + //Find the total length field since it is units of 8 octets and discard if less than RDNSSMINLEN. + TUint8 opt_len = (length)* RDNSSOPTION_HDRLENGTH; + if(opt_len < RDNSSMINLEN) + { + return EFalse; + } + else + { + aNumRdnssAddr = (length-1)/2; + return ETrue; + } + } + +//RFC 5006 Changes for RDNSS_OPTION +#endif //SYMBIAN_TCPIPDHCP_UPDATE + + +// *********** +// CIp6Address +// *********** +// Holds additional id's assigned to this interface, to be used for +// generated ids as described in privacy extension RFC-3041. +// +class CIp6Address : public CBase + { +public: + TIp6AddressInfo iInfo; + }; + +// ************* +// CIp6Interface +// ************* + +class CIp6Interface : public CBase, public MInterface + { + friend class CIp6Flow; + friend class CIp6Manager; + friend class CIp6NifUser; + friend class CIp6Route; + // ********* + // *WARNING* + // ********* + // When adding members/fields into this class, do remember + // to check the "reset information" in Reset() method, + // if value needs to be cleared between interfaces reusing + // this same structure! -- msa + // +public: + CIp6Interface(CIp6Manager &aMgr, TUint aIndex, const TDesC &aName); + ~CIp6Interface(); + + TUint32 Index() const; + const TDesC &Name() const; + TUint32 Scope(const TScopeType aType) const; + + void Reset(const TInt aKeepNif = 0);// set instance back to initial state. + + void Timeout(const TTime &aStamp); // Timer expiration event handler + + + CIp6Route *SelectSource(TIp6Addr &aSrc, const TIp6Addr & aDst) const; + void SetPrefix(const TIp6Addr &aPrefix, const TUint aLength, const TInt aForce, const TLifetime aLifetime = KLifetimeForever, const TLifetime aPeferred = KLifetimeForever); + + // + // Return the number of seconds between the given time (aStamp) and + // the time when interface was activated (iTimeStamp). aStamp must + // always be same of after iTimeStamp. + TLifetime Elapsed(const TTime &aStamp) const; + + // + // Methods for the Id part of the Ip6 addresses + // + void UpdateIdRoutes(const TIp6AddressInfo &aId, const TLifetime aLifetime); + TInt SetId(TIp6AddressInfo &aId, const TIp6Addr &aAddr, const TInt aPrefix, const TInt aAddressType); + TInt AddId(const TSockAddr& aId); // returns 0 = not changed, 1 = changed + TInt AddId(const TIp6Addr &aId, const TInt aPrefix, const TInt aAddressType = TIp6AddressInfo::ENormal, const TBool aForcePrimary = EFalse); // returns 0 = not changed, 1 = changed + TIp6AddressInfo* GetId(const TIp6Addr &aAddr) const; + TInt RemId(const TIp6AddressInfo *const aId); + // SetMtu sets the send MTU. Currently called from RouterAdvert handler + // and it might be dubious thing to unconditionally change the interface + // send Mtu this way (could perhaps constrain it by the interface reported + // value). CHECK THIS LATER! -- msa + void SetMtu(TInt aMtu, TInt aMin); + // StartSending handles the StartSending from the interface + TInt StartSending(); + inline TInt IsNetdial() const {return iName.Length() == 0; } + inline TInt NeedsND() const { return iFeatures & KIfNeedsND; } + // ...can send RS only if IPv6 enabled and supports multicast + inline TInt CanSendRS() const { return iIsIPv6 && (iFeatures & KIfCanMulticast); } + // + // IsMyAddress returns non-NULL, if aAddr matches any of the + // current src addresses for this interface. The returned ptr + // indicates the ID that matched. + // + // Normally proxy and anycast addresses are not "my addresses", but + // neigbour discovery needs to treat them as own, thus allow aAll != 0 + // to include them into "my address".. + // + TIp6AddressInfo *IsMyAddress(const TIp6Addr &aAddr, const TInt aAll = 0) const; + // IsForMeAddress returns TRUE, if aAddr is for me (almost + // same as IsMyAddr, but additionally returns true for multicast + // and anycast addresses. + TBool IsForMeAddress(const TIp6Addr &aAddr) const; + // + // IsMyId returns non-NULL, if aAddr matches any of the + // id's for the interface (also tentative ones!) + TIp6AddressInfo *IsMyId(const TIp6Addr &aAddr) const; + // + // IsMyPrefix returns non-NULL, if aAddr matches any of + // the MYPREFIX entries in the route list. + CIp6Route *IsMyPrefix(const TIp6Addr &aAddr, const TIp6AddressInfo &aId) const; + // + // Update flow counts (iFlows). Change can positive + // or negative. + // + void UpdateFlowCount(TInt aChange); + // Send a packet to the interface + TInt Send(RMBufChain& aPacket, CProtocolBase* aSourceProtocol=NULL); + TInt UpdateMulticast(const TIp6Addr &aMulticast, TLifetime const aLifetime = KLifetimeForever); + void GetDefGateway(const TBool aIsIPv4, TInetAddr &aAddr) const; + CIp6Route *GetRoute(const TIp6Addr &aAddr, TInt aPrefix, TUint aFlags, const TSockAddr *const aGateway = NULL, const TLifetime *const aLifetime = NULL); + void RemoveRoute(CIp6Route *aRoute); + void MoveToFront(CIp6Route *aRoute); // Move the route to the first in the list + void NotifyFlows(TInt aState, TBool aForce = EFalse) const; // External change in interface/driver + void NotifyFlowsPmtu(const TUint aPmtu) const; // Notify attached flows about changed Path MTU + TInt SetChanged(const TInt aScope = 0) const;// Set iChanged on attached flows + CIp6Route *SelectNextHop(const TIp6Addr &aDst, const TIp6Addr &aSrc, CIp6Route *aRoute); + + CIp6Route *FindNeighbor(const TIp6Addr &aDst) const; + CIp6Route *FindRoute(const TIp6Addr &aDst, CIp6Route *aRoute) const; + + // + // Accessing the main components uniformly independent of the class + // (when linkages between classes change, just change these to reflect + // the change, and the rest of the code should work unchanged) + // + inline CIp6Manager &Interfacer() const { return iInterfacer; }; + // + // Set/reset a timer event on the current object + // + void SetTimer(TUint32 aDelay) { Interfacer().SetTimer(iTimeout, aDelay); } + // CancelTimer/IsTimerActive are just syntactic sugar, because + // of the SetTimer: if one uses SetTimer and hides iTimeout, then + // all uses of iTimeout should be "hidden" too! + inline void CancelTimer() { iTimeout.Cancel(); } + inline TBool IsTimerActive() { return iTimeout.IsActive(); } + TInt HaveIp4LinkLocal(); + TBool HasIpv4LinkLocalAddr() const { return FindIpv4LinkLocalAddr() ? ETrue : EFalse; } + TInt SetIpv4LinkLocal(TUint aFlag); + const TIp6AddressInfo* FindIpv4LinkLocalAddr() const; + + // Values given by HaveIp4LinkLocal(), equal to possible tcpip6.ini configuration settings. + enum EV4LLEnums + { + EV4LLDisabled = 0, //< Do not use IPv4 link-local addresses in any case. + EV4LLAlways, //< Use IPv4 link-local address whenever possible. + EV4LLConditional, //< Use IPv4 link-local address if Nif does not have configured address. + EV4LLConfigDaemonControlled, //< Do not use IPv4 link-local address if we succeed in acquiring an IP from a server (e.g., DHCP). If a server is not present or unavailable, automatically configure link-local address. + EV4LLUnknown //< Status of IPv4 link-local setting is unknown (ini file haven't been read yet). + }; + CIp6Route *StartProbeND(const TIp6Addr &aSrc, const TIp6Addr &aDst); + +private: + // DoBind is called when NifIfBase instance becomes available + TInt DoBind(CIp6NifUser *aNifUser, CNifIfBase *aIf); + TInt RandomAddress(TIp6Addr &aAddr, TUint aPrefix, TUint aN); + void DuplicateAddress(TIp6AddressInfo *aId, TBool &aDefendIPAddress, const TBool aGratuitousArp = EFalse); + TInt ConfigureAddress(const TIp6Addr &aAddr, const TUint aMaskLength, const TBool aForcePrimary = EFalse); + TInt ConfigureLinkLocal(TUint32 aConfAddr); + TIp6AddressInfo* FindInternalIpv4LinkLocalAddr(); + void UpdateNameServers(const TInetAddr &ns1, const TInetAddr &ns2, const TInt aOverride = 0); + TInt Update4(TInt aTransition); // Try IPv4 specific setup + TInt Update6(TInt aTransition); // Try IPv6 specific setup + TInt SendNeighbors(TInt aMessageType, CIp6Route *aDestination, const TIp6Addr &aTarget, const TIp6Addr *const aSrc = NULL); + TInt IcmpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, TInet6Packet &aNd); +#ifdef ARP + TInt ArpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, TInet6Packet &aArp); +#endif + void Ip4RedirectHandler(const RMBufRecvPacket &aPacket, const RMBufRecvInfo &aInfo); + + void SetReachableTime(); + void SetRetransTimer(); + void RouterChanged(CIp6Route *const aRouter); + void SetAddressAndScope(TSockAddr &aAddr, const TSockAddr &aSrc) const; + + void NotifyAddressEvent(TUint aEventType, const TIp6Addr &aPrefix, + const TUint aLength, + const CIp6Route *aPrefixEntry, const TIp6AddressInfo &aAddress) const; + + // Send notification about changed route to event manager + void NotifyRouteEvent(TUint aEventType, const CIp6Route *aRoute, const TLifetime aLifetime = 0) const; + + void NotifyInterfaceEvent(TUint aEventType) const; + + void NotifyMulticastEvent(TUint aEventType, const TIp6Addr &aMulticast, const TLifetime aLifetime) const; +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + //Do DAD for the Global address + void PerformDADForGlobalAddress(const TIp6Addr &aPrefix,const TUint aLength); +#endif //SYMBIAN_TCPIPDHCP_UPDATE + + CIp6Manager &iInterfacer; // + const TName iName; //< The name of the interface. + TInt iState; //< Interface state: PENDING, READY, HOLD or DOWN. + TTime iTimeStamp; //< Base Time Reference for address lifetimes. + TUint iSequence; //< Incremented once for each address deleting event. + TIp6AddressInfo iAddress; //< Assigned addresses. + CIp6Route *iRouteList; //< All routes. + TInt iSMtu; //< Send MTU (cached value, could also just ask it always from the interface) + TInt iRMtu; //< Receive MTU (cached value, could also just ask it always from the interface) + TInt iSpeedMetric; //< (cached value from the interface) + TUint iFeatures; //< (cached value from the interface) + TInetAddr iNameSer1; //< 1. Name server address (if defined) + TInetAddr iNameSer2; //< 2. Name server address (if defined) + TInt iPMtu; //< Path MTU for this interface + TUint iRouters; //< Current number of routers + TUint8 iRetryRS:8; //< ...only used in startup for RS sols. + TUint8 iHopLimit:8; //< Current Default Hoplimit on this link + TUint iIsIPv6:1; //< Interface configured for IPv6, if set + TUint iIsIPv4:1; //< Interface configured for IPv4, if set + TUint iIsSuspended:1; //< TRUE if interface is suspended. + // Use of link-local IPv4 addresses. 0: link-locals disabled, 1: use if no IPv4 address is read from Nif, + // 2: always attach link-local address to interface, 3: use link-local address if no IPv4 address is read from Nif or configuration daemon + TUint iIpv4Linklocal:3; + + /** Set IS ROUTER flag to neighbour advertisement message. + // Note! This is a low level functionality flag, and does not have any + // other semantics, like enabling general router functionality. + */ + TUint iIsRouter:1; + + TInetNdConfig iND; //< Current Neighbor Discovery parameters (base values) + TUint iReachableTime; //< User::TickCount units, computed from base values + TUint iRetransTimer; //< Timer units, computed from base values + /** + // Hardware address of the interface. + // @li + // if iHwAddr.Family() is KAFUnspec (= 0), then link layer does not support + // link layer addresses + // @li + // if iHwAddr.Family() is not KAFUnspec, then this contains the current + // link layer address of this interface (if known). Also, the Family + // is the assumed address family of the link layer addresses of the + // other nodes on this link. + */ + TLinkAddr iHwAddr; + CNifIfBase *iNifIf; //< Interface instance + /** + // Held packets when interface has blocked (iState == EFlow_HOLD). + // This queue should normally be ALWAYS empty. It only gets used + // when some component of the system does not honour the "flow + // blocking" singal (return 0 from Send). + // + // Currently, IP fragmenter is such component due to posthooks. + // It would have hard time handling the situation (because it + // cannot be sure which interface the packets actually end up!) + */ + RMBufPktQ iHoldQueue; + // + // CIp6Manager Work Space + // + TInt iFlows; //< Number of flows leading to this interface from routes + CIp6Interface *iNext; //< Interface List Link (head in CIp6Manager) + // + // iNifUser always points to one of the MNifIfUser instances within + // CIp6Manager. It is initialized in when interface is created and + // only updated in DoBind(). + // [the need for this needs to be re-examined -- msa] + // + CIp6NifUser *iNifUser; + // The Scope Identifiers assigned to this interface + TInetScopeIds iScope; +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + CManageRdnssServerList *iRdnssList; //RFC-5006 + // Global flag, used while composing global address from prefix of RA (with 'A' flag set) + TBool iGlobalflag; +#endif //SYMBIAN_TCPIPDHCP_UPDATE +public: // GCC doesn't compile CIp6InterfaceLinkage, if this is private! -- msa + RTimeout iTimeout; //< Hook to the timer service (MTimeoutManager) + }; + +// +// CIp6InterfaceTimeoutLinkage +// *********************** +// Glue to bind timeout callback from the timeout manager into Timeout() call +// on the CIp6Route +// +// *NOTE* +// This kludgery is all static and compile time, and only used in the constructor +// of CIp6Interface. +// + +// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be +// passed as a template parameter +#if defined(__X86GCC__) || defined(__GCCE__) +#define KIp6InterfaceTimeoutOffset 696 +__ASSERT_COMPILE(KIp6InterfaceTimeoutOffset == _FOFF(CIp6Interface, iTimeout)); +#else +#define KIp6InterfaceTimeoutOffset _FOFF(CIp6Interface, iTimeout) +#endif + +class CIp6InterfaceTimeoutLinkage : public TimeoutLinkage + { +public: + static void Timeout(RTimeout &aLink, const TTime &aNow, TAny * /*aPtr*/) + { + LOG(Log::Printf(_L("<>\tIF Timeout"))); + Object(aLink)->Timeout(aNow); + } + }; + +// +// ********* +// CIp6Route +// ********* +// Map address to specific gateway and interface +// + +// The main type of the route is expressed with 2 bits. However, in some +// cases system may need routes that are distinguished in GetRoute as +// distinct entries, but work the same everywhere else. Only one bit +// is reserved for that purpose now... (and used by ERedirect) +const TUint KRouteAdd_EXTENSIONMASK = (1 << 2); +const TUint KRouteAdd_SHIFT = 3; + +class CIp6Route : public CBase + { + friend class CIp6Flow; + friend class CIp6Manager; + friend class CIp6Interface; + friend class CIp6NifUser; + friend class CIp6RouteTimeoutLinkage; +public: + CIp6Route(TUint aIndex, CIp6Manager &aMgr, const TIp6Addr &aAddr, TInt aPrefix, CIp6Interface &aInterface); + ~CIp6Route(); + TInt Match(const TIp6Addr &aAddr) const; + void Attach(CIp6Flow &aFlow); // + void Attach(CIp6Route &aRoute); // "Steal" all flows from another route + void Detach(CIp6Flow &aFlow); + void NotifyFlows(TInt aStatus); + TInt SetChanged(const TInt aScope = 0) const; // Set iChanged on attached flows + + enum TState + { + // + // The first 4 states must *exactly* match the route type + // value of the flags parameter in AddRouteL method! + // + // New CIp6Route can be created only into these 4 states + // + EIncomplete = KRouteAdd_NEIGHBOR, //< == 0 (MUST BE ZERO) + ELoopback = KRouteAdd_MYPREFIX, //< == 1 + EOnlink = KRouteAdd_ONLINK, //< == 2 + EGateway = KRouteAdd_GATEWAY, //< == 3 + /** + // ERedirect is a special variant of a Gateway generated by + // the ICMP Redirects (mostly works exactly as a gateway) + */ + ERedirect = KRouteAdd_EXTENSIONMASK | KRouteAdd_GATEWAY, + // EAnycast is a special variant of a Loopback enty + EAnycast = KRouteAdd_EXTENSIONMASK | KRouteAdd_MYPREFIX, + // The remaining states are only entered from EIncomplete + // if Neighbor discovery is applicable for the interface + // (all of these must have the low 2 bits zero, to + // make all of them as host routes (Type() == 0). + // + EReachable = 1 << KRouteAdd_SHIFT, + EStale = 2 << KRouteAdd_SHIFT, + EDelay = 3 << KRouteAdd_SHIFT, + EProbe = 4 << KRouteAdd_SHIFT, + + // A unique state for the fixed HoldingRoute + EHolding = 7 << KRouteAdd_SHIFT + }; + + TUint ExtendedType() const + { + return iState & (KRouteAdd_EXTENSIONMASK | KRouteAdd_TYPEMASK); + } + + TUint Type() const + { + return iState & KRouteAdd_TYPEMASK; + } + TBool IsHoldingRoute() const + { + return iState == EHolding; + } + inline TBool IsOnlink() const + /** Matching address(es) is/are Onlink */ + { + return Type() == KRouteAdd_ONLINK; + } + inline TBool IsGateway() const + /** Matching addresses should be sent to the gateway. */ + { + return Type() == KRouteAdd_GATEWAY; + } + inline TBool IsHostRoute() const + /** Matching address is onlink with specified link layer address. */ + { + return Type() == KRouteAdd_NEIGHBOR; + } + inline TBool IsMyPrefix() const + { + return iState == ELoopback && !iIsMulticast; // Note: Does no match EAnycast! + } + void Timeout(const TInt aExpired = 0); + // + // Accessing the main components uniformly independent of the class + // (when linkages between classes change, just change these to reflect + // the change, and the rest of the code should work unchanged) + // + inline CIp6Manager &Interfacer() const { return iInterfacer; }; + // + // Set/reset a timer event on the current object + // + void SetTimer(TUint32 aDelay) { Interfacer().SetTimer(iTimeout, aDelay); } + // CancelTimer/IsTimerActive are just syntactic sugar, because + // of the SetTimer: if one uses SetTimer and hides iTimeout, then + // all uses of iTimeout should be "hidden" too! + inline void CancelTimer() { iTimeout.Cancel(); } + inline TBool IsTimerActive() { return iTimeout.IsActive(); } +#ifdef _LOG + const TDesC &LogRouteType() const; + // + // Generate a log message from current route state + // + void LogRoute(const TLifetime aLifetime) const; +#endif + +private: + const TUint iIndex; //< Assigned Route index (always > 0) + CIp6Manager &iInterfacer; //< The interface Manager + // + // The "address/prefix" + // + TIp6Addr iPrefix; //< Address prefix for this route + TUint8 iLength; //< Length of the prefix (bits) + TState iState:8; //< Current State + TUint iRetry:8; //< Tracks various retransmissions (depend on iState) + TUint iIsMulticast:1; //< True if prefix is multicast address (precomputed for optim.) + TUint iIsRouter:1; //< = 0, no other route may point to this (iRouter) + TUint iIsProbing:1; //< = 1, if is probing route (not found by FindRoute) + TInt iMetric; //< Metric value of the route (smaller is better) + TUint iTimeStamp; //< Interpretation depends on iState + RMBufChain iPacket; //< A packet waiting for ND completion, if any. +public: // GCC doesn't compile CIp6RouteLinkage, if this is private! -- msa + RTimeout iTimeout; //< Hook to the timer service (MTimeoutManager) +private: + TInt Send(RMBufChain& aPacket, CProtocolBase* aSourceProtocol=NULL, TInt aMulticastLoop = 0); + void StartND(const TIp6Addr &aSrc); + TInt Update(TInt aFlags, const TSockAddr *aGateway, const TLifetime *const aLifetime); + void UpdatePrefix(const TLifetime aLifetime, const TLifetime aPreferred); + + void FillRouteInfo(TInetRouteInfo &rinfo, TLifetime aRefTime) const; + void FillNeighbourInfo(TInetNeighbourInfo &nginfo, TLifetime aRefTime) const; + + struct TIp6LifetimeField + { + TLifetime iStored; //< Stored Lifetime (relative to interface startup) + TLifetime iPreferred; //< "deprecated lifetime" in seconds + TUint iDeprecated:1; //< = 1, when in "deprecated state" + TUint iCount; //< Only used for multicast entries (for now) + }; + + union + { + // + // Type() != ELoopback + // + // Note: Cannot use TSockAddr derived class, as it would generate a + // constructor and cannot use it here (inside union) -- msa + // + TRouteAddress iAddress; + // + // Type() == ELoopback + // + TIp6LifetimeField iLifetime; + }; + CIp6Route *iRouter; //< Link to the Router entry, when it exists + // + // Linking of all routes on the interface + // + CIp6Interface &iInterface; //< Interface definition + CIp6Route *iNext; //< Route List Link (head in CIp6Interface) + CIp6Flow *iFlowList; //< Book keeping of flows using this route + }; + +// +// CIp6RouteTimeoutLinkage +// *********************** +// Glue to bind timeout callback from the timeout manager into Timeout() call +// on the CIp6Route +// +// *NOTE* +// This kludgery is all static and compile time, and only used in the constructor +// of CIp6Route following this. +// + +// This ungainly manoevure is forced on us because the offset is not evaluated early enough by GCC3.4 to be +// passed as a template parameter +#if defined(__X86GCC__) || defined(__GCCE__) +#define KIp6RouteTimeoutOffset 44 +__ASSERT_COMPILE(KIp6RouteTimeoutOffset == _FOFF(CIp6Route, iTimeout)); +#else +#define KIp6RouteTimeoutOffset _FOFF(CIp6Route, iTimeout) +#endif + +class CIp6RouteTimeoutLinkage : public TimeoutLinkage + { +public: + static void Timeout(RTimeout &aLink, const TTime & /*aNow*/, TAny * /*aPtr*/) + { + LOG(Log::Printf(_L("<>\tROUTE Timeout"))); + Object(aLink)->Timeout(1); // aExpired==1 to signal true expiration + } + }; + +// +// TFlowOptions +// ************ +// A dubious collection of fields that are mainly set +// by set options. +// +class TFlowOptions + { +public: + TInt16 iHopLimit; //< Hoplimit/TTL for non-multicast packets + TInt16 iMulticastHops; //< Hoplimit/TTL for multicast packets + TUint8 iTrafficClass; // + TUint iDF:1; + TUint iMulticastLoop:1; //< 0=Don't loopback multicasts, 1=do loopback + // The interface flow count controls the NIF OpenRoute/CloseRoute calls, + // iKeepInterfaceUp controls whether this flow affects that count (the + // default is 0). + TUint iKeepInterfaceUp:1; //< 0=Don't count, 1= count flow against inteface flow count + + // Note! Cannot use TScopeType below, because it would make the + // bitfield into signed and fail on tests like: + // x.iLockType == EScopeType_NET + // even if x.iLockType has value EScopeType_NET!!! -- msa + /** + * Locked scope-1 (0..15) [TScopeType]. + * Initialized from upper layer value at the beginning of the + * Connect. Cleared after use and must be "reactivated" by + * the hook, if it needs it. [No API this exists now] + */ + TUint iLockType:4; + /** + * Current Locking Id. + * Initialized from upper layer value at the beginning of the + * Connect. Cleared after use and must be "reactivated" by + * the hook, if it needs it. [No API this exists now] + */ + TUint32 iLockId; + }; + +// ********* +// TListLink +// ********* +class TListLink + /** + * A base class of a simple double linked list. + * + * This implmentain does not require the 'offset' like TDblQueBase. + * Also, NULL is never used, links are always non-NULL and if list + * is empty (or element is not part of any list), + * the links point to self. + * + * At this level, there is no difference between list head and + * an element. + */ + { +public: + inline TListLink() + /** + * Constructor. + * + * TListLink is automaticly created as "detached", linked to self. + */ + { + iPrev = this; + iNext = this; + } + inline ~TListLink() + /** + * Desctructor. + * + * TListLink(s) can be declared as a member of any class + * and there is no need to worry about instance being a + * member of lists when it is destroyed. This destructor + * automaticly removes the instance from a list if inserted. + */ + { + Detach(); + } + inline void MoveTo(TListLink &aList) + /** + * Move element to another list. + * + * MoveTo removes this element from previous list (if any) + * and inserts it to a new list, in front of the specified + * element (aList). + */ + { + // Does not work if moving to self! + ASSERT(this != &aList); + if (this == &aList) + return; + + // Remove element from the old queue + iPrev->iNext = iNext; + iNext->iPrev = iPrev; + // Add to aList + iNext = &aList; + iPrev = aList.iPrev; + iPrev->iNext = this; + iNext->iPrev = this; + } + + inline void Detach() + /** Detach this element from a list (if any). */ + { + iPrev->iNext = iNext; + iNext->iPrev = iPrev; + iNext = this; + iPrev = this; + } + +protected: + TListLink *iPrev; + TListLink *iNext; + }; + + +// ******** +// CIp6Flow +// ******** +// +class TFlowNotifyList; +class CIp6Flow : public CFlowInternalContext + { + friend class CIp6Manager; + friend class CIp6Route; + friend class CIp6Interface; + friend class CIp6NifUser; + friend class TFlowNotifyList; +public: + CIp6Flow(const void *aOwner, MFlowManager *aManager, CIp6Manager &aInterfacer, TUint aProtocol); + CIp6Flow(const void *aOwner, MFlowManager *aManager, CIp6Manager &aInterfacer, CFlowContext &aFlow); + virtual ~CIp6Flow(); + virtual MInterfaceManager *Interfacer() const + { return &iInterfacer; } + virtual CNifIfBase *Interface() const; + virtual TInt Send(RMBufChain& aPacket, CProtocolBase* aSourceProtocol=NULL); + virtual void RefreshFlow(); + virtual void Connect(); + virtual TInt RouteFlow(TPacketHead &aHead); + virtual void Disconnect(); + virtual TInt InterfaceSMtu() const; + virtual TInt InterfaceRMtu() const; + virtual TInt GetOption(TUint aLevel, TUint aName, TDes8 &aOption) const; + virtual TInt SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption); + + void Notify(TFlowNotifyList &aList, const TInt aState); + TInt SetChanged(const TInt aScope = 0); // Set iChanged +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + virtual TBool IsNdPacketPendingResolution();//RFC 4861 Changes +#endif //SYMBIAN_TCPIPDHCP_UPDATE +private: + TInt VerifyAddress(const TPacketHead &aHead, const CIp6Interface &aIf) const; + void SelectNextHop(); + +#ifdef SYMBIAN_NETWORKING_UPS + TBool UPSPromptingPossible(); + TBool ApplyStaticSecurityCheck(); +#endif + CIp6Manager &iInterfacer; // (i) Interface Manager + CIp6Route *iRoute; // (i) Attached route + CIp6Flow *iNext; // (i) Other flows on same route + TFlowOptions iOptions; // (m) + TUint iSequence; // (-) Used to detect expired source address. + TUint iTimeStamp; // (-) Currently, only used when in HoldingRoute + TListLink iNotifyList; +#ifdef SYMBIAN_NETWORKING_UPS + TBool iUpsAuthorisationRequired; + TBool iUpsAuthorisationPending; +#endif + }; + + +#ifdef _LOG + +// +// Internal utility to return a symbolic scope level name +// (only for DEBUG compile) +static const TDesC &LogScopeName(TInt aScope) + { + _LIT(KScope_0, "IF"); // 1 interface (node local) + _LIT(KScope_1, "IAP"); // 2 link local + _LIT(KScope_2, "SC3"); // 3 + _LIT(KScope_3, "SC4"); // 4 + _LIT(KScope_4, "SITE"); // 5 site local + _LIT(KScope_5, "SC6"); // 6 + _LIT(KScope_6, "SC7"); // 7 + _LIT(KScope_7, "ORG"); // 8 organization + _LIT(KScope_8, "SC9"); // 9 + _LIT(KScope_9, "SC10"); // 10 + _LIT(KScope_10, "SC11"); // 11 + _LIT(KScope_11, "SC12"); // 12 + _LIT(KScope_12, "SC13"); // 13 + _LIT(KScope_13, "GBL"); // 14 global + _LIT(KScope_14, "SC15"); // 15 + _LIT(KScope_15, "NET"); // -- network + + // Some tricky type casting is required to get rid of the + // writable static data problem at target linking... +# define CAST(x) &reinterpret_cast(x) + static const TDesC *const map[] = + { + CAST(KScope_0), CAST(KScope_1), CAST(KScope_2), CAST(KScope_3), + CAST(KScope_4), CAST(KScope_5), CAST(KScope_6), CAST(KScope_7), + CAST(KScope_8), CAST(KScope_9), CAST(KScope_10), CAST(KScope_11), + CAST(KScope_12), CAST(KScope_13), CAST(KScope_14), CAST(KScope_15) + }; +# undef CAST + + + return *map[aScope & 0xF]; + } + + +// +// Internal utility for formatting address/prefix. Only for DEBUG compile +// +class TLogAddressPrefix : public TBuf<70> + { +public: + TLogAddressPrefix() {} + /** Format plain address%scope. */ + TLogAddressPrefix(const TIp6Addr &aAddr); + /** Format address%scope/prefix. */ + TLogAddressPrefix(const TIp6Addr &aAddr, const TInt aPrefix); + /** Format address%scope#port */ + TLogAddressPrefix(const TInetAddr &aAddr); + /** Format plain address%scope. */ + void Set(const TIp6Addr &aAddr); + /** Format address%scope/prefix. */ + void Set(const TIp6Addr &aAddr, const TInt aPrefix); + /** Format address%scope#port */ + void Set(const TInetAddr &aAddr); + }; + +TLogAddressPrefix::TLogAddressPrefix(const TIp6Addr &aAddr) + { + Set(aAddr); + } + +TLogAddressPrefix::TLogAddressPrefix(const TIp6Addr &aAddr, const TInt aPrefix) + { + Set(aAddr, aPrefix); + } + +TLogAddressPrefix::TLogAddressPrefix(const TInetAddr &aAddr) + { + Set(aAddr); + } + +void TLogAddressPrefix::Set(const TIp6Addr &aAddr) + { + const TInetAddr addr(aAddr, 0); + addr.OutputWithScope(*this); + } + +void TLogAddressPrefix::Set(const TIp6Addr &aAddr, const TInt aPrefix) + { + Set(aAddr); + _LIT(KFormat, "/%d"); + // Note: the overflow check is omitted on purpose (because + // leaving information out silently would cause more confusion). + // The supplied buffer should always be sufficient. + AppendFormat(KFormat, aPrefix - (aAddr.IsV4Mapped() ? 96 : 0)); + } + +void TLogAddressPrefix::Set(const TInetAddr &aAddr) + { + if (aAddr.Family() == KAfInet || aAddr.Family() == KAfInet6 || aAddr.Family() == KAFUnspec) + aAddr.OutputWithScope(*this); + else + { + // Assume some type of link layer address, dump octets as xx:xx:... + SetLength(0); + const TPtrC8 ptr = TLinkAddr::Cast(aAddr).Address(); + const TInt N = ptr.Length(); + if (N > 0) + { + AppendNum((TInt)ptr[0], EHex); + for (TInt i = 1; i < N; ++i) + { + Append(':'); + AppendNum((TInt)ptr[i], EHex); + } + } + } + if (aAddr.Port()) + { + _LIT(KFormat, "#%d"); + AppendFormat(KFormat, aAddr.Port()); + } + } + +void PktLog(const TDesC &aFormat, const RMBufPktInfo &aInfo, TUint aIndex, const TDesC &aName) + { + TLogAddressPrefix src(TInetAddr::Cast(aInfo.iSrcAddr)); + TLogAddressPrefix dst(TInetAddr::Cast(aInfo.iDstAddr)); + Log::Printf(aFormat, aIndex, &aName, aInfo.iProtocol, &src, &dst, aInfo.iLength); + } + +#endif + + +class TFlowNotifyList : public TListLink + { +public: + void Insert(CIp6Flow &aFlow); + void Deliver(TInt aStatus); + }; + + +void TFlowNotifyList::Insert(CIp6Flow &aFlow) + { + aFlow.iNotifyList.MoveTo(*this); + } + +void TFlowNotifyList::Deliver(TInt aStatus) + { + TListLink *p; + while ((p = iNext) != this) + { + p->Detach(); + CIp6Flow *const f = (CIp6Flow *)((TUint8 *)p - _FOFF(CIp6Flow, iNotifyList)); +#ifdef _LOG + { + TLogAddressPrefix src(f->iInfo.iLocal); + TLogAddressPrefix dst(f->iInfo.iRemote); + Log::Printf(_L("\t\tFlow[%u] Deliver(%d -> %d) prot=%d src=[%S], dst=[%S]"), + (TInt)f, aStatus, f->iStatus, (TInt)f->iInfo.iProtocol, &src, &dst); + } +#endif + f->SetStatus(aStatus); + } + } + + + +// +// Internal help utility to retrieve tick period as unsigned int +// +static TUint TickPeriod() + { + TTimeIntervalMicroSeconds32 period; + UserHal::TickPeriod(period); + return (TUint)period.Int(); + } + +static TUint ElapsedUnits(const TTime &aMark, const TTime &aLater) + /** + * Internal help utitility to compute the difference between two timestamps. + * + * @param aMark The earlier time stamp + * @param aLater The later time stamp + * + * @return + * (aLater - aMark) in timer units, or KMaxTUint if + * the value does not fit in TUint. + */ + { + const TInt64 elapsed = aLater.MicroSecondsFrom(aMark).Int64() / (1000000 / TIMER_UNIT); +#ifdef I64HIGH + return I64HIGH(elapsed) != 0 ? KMaxTUint : I64LOW(elapsed); +#else + return elapsed.High() != 0 ? KMaxTUint : elapsed.Low(); +#endif + } + + +// ********** +// CIp6Daemon +// ********** +class CIp6Daemon : public CBase + /** + * Keep track of active daemons related to the protocol stack + */ + { +public: + ~CIp6Daemon(); + void Start(const TDesC &aProcessName, const TDesC &aFileName); + void Kill(); + CIp6Daemon *iNext; + RProcess iProcess; + TUint iStarted:1; //< =1, if handle is attached to another process/thread + }; + +// *********** +// ~CIp6Daemon +// *********** +CIp6Daemon::~CIp6Daemon() + /** Also automaticly kills the attached process, if any running. */ + { + Kill(); + } + +// **************** +// CIp6Daemon::Kill +// **************** +void CIp6Daemon::Kill() + /** Kill thread/process, if running and disconnect handle from the thread/process. */ + { + if (iStarted) + { + iStarted = 0; + if (iProcess.ExitType() == EExitPending) + iProcess.Kill(KErrServerTerminated); + iProcess.Close(); + } + } + +// ***************** +// CIp6Daemon::Start +// ***************** +// +void CIp6Daemon::Start(const TDesC &aProcessName, const TDesC &aFileName) + /** Start the named EXE as a daemon. */ + { + ASSERT(!iStarted); + + TInt res = iProcess.Create(aFileName, _L("")); + if (res == KErrNone) + { + iStarted = 1; + iProcess.Resume(); + } + +#ifdef _LOG + if (iStarted) + Log::Printf(_L("CIp6Daemon::Start(%S, %S) OK"), &aProcessName, &aFileName); + else + Log::Printf(_L("CIp6Daemon::Start(%S, %S) *** FAILED *** with error %d"), &aProcessName, &aFileName, res); +#else + (void)aProcessName; // prevent warning message +#endif + } + +// TIp6AddressInfo::Match +// ********************** +TBool TIp6AddressInfo::Match(const TIp6Addr &aAddr) const + /** + * Match ID part. + * + * @param aAddr The id to compare with. + * + * Match returns TRUE, if aAddr matches the + * ID/hostnumber (tailored after similar code in + * TIp6Addr::Match() ) + */ + { + ASSERT(iPrefix <= 128); + if (iPrefix > 127) // (actually, == 128) + return TRUE; + + TInt i = 3; + while (iId.u.iAddr32[i] == aAddr.u.iAddr32[i]) + if (i == 0) + return TRUE; // Obviously true, regardless of the iPrefix + else + --i; + // Optimize for 64 bit id? + // if (iPrefix >= 64 && i < 2) return TRUE; + + i = i * 2 + 1; + if (iId.u.iAddr16[i] == aAddr.u.iAddr16[i]) + --i; + + i = i * 2 + 1; + if (iId.u.iAddr8[i] == aAddr.u.iAddr8[i]) + --i; + + // i = index of the byte containing a difference, the + // number of unmatched bits is (i+1) * 8 - "matched bits + // in the current byte". ...count them below + // + TUint8 diff = (TUint8)(iId.u.iAddr8[i] ^ aAddr.u.iAddr8[i]); + for (i = (i + 1) << 3; !(diff & 0x1); diff >>= 1) + --i; + // Matched full id part? + return iPrefix >= i; + } + +// TIp6AddressInfo::MatchExactly +// ********************** +TBool TIp6AddressInfo::MatchExactly(const TIp6Addr &aAddr) const + /** + * Match ID part only. + * + * @param aAddr The id to compare with. + * + * Match returns TRUE, if aAddr matches the + * ID/hostnumber (tailored after similar code in + * TIp6Addr::Match() ) + */ + { + ASSERT(iPrefix <= 128); + + // If any address bits are ignored, return false as the address + // cannot be an exact match. + if( iPrefix != 0 ) + { + return EFalse; + } + + // Determine the size of the address. + TUint addrBits = sizeof( iId.u )/ sizeof( TUint ); + TUint addrWords = addrBits; + if( iId.IsV4Mapped() ) + { + addrWords = 1; + } + + + // Check every address word. + for( TUint i = ( addrBits ) - addrWords; i < addrBits; ++i ) + { + if( aAddr.u.iAddr32[i] != iId.u.iAddr32[i] ) + { + return EFalse; + } + } + + return ETrue; + } + + +// ********************************************** +// CIp6Flow, the flow termination implementations +// ********************************************** + +// +// Interaface +// ********** +// Return connected interface +CNifIfBase *CIp6Flow::Interface() const + { + // + // Is this flow actually connected? + // + if (iRoute) + return iRoute->iInterface.iNifIf; + else + return NULL; + } + + +// +// CIp6Flow::CIp6Flow +// ****************** +// *BEWARE* +// iManager link is set here, but there is no path from +// the manager to this until it is connected (after which +// it can be found via routes). If manager is destroyed, +// it is possible to have dangling pointers from +// unconnected flows! +// +// However, the current assumption is that all flows are +// allocated within this same protocol library and all +// them must have been deleted before the family object +// and the associated CIp6Manager gets deleted. +// +// The iFlows count in the CIp6Manager is maintained only +// to catch programming errors (and a Panic is issued). + +CIp6Flow::CIp6Flow(const void *aOwner, MFlowManager *aManager, CIp6Manager &aInterfacer, TUint aProtocol) +: CFlowInternalContext(aOwner, aManager), iInterfacer(aInterfacer) + { + iInterfacer.iFlows++; + iInfo.iProtocol = (TUint8)aProtocol; + // Init with default "interface error handling" policy + iInfo.iNoInterfaceError = iInterfacer.iNoInterfaceError; + // By default, lock flows to any network + iInfo.iLockId = 0; + iInfo.iLockType = EScopeType_NET; + // Init default for flow counting on interfaces policy + iOptions.iKeepInterfaceUp = iInterfacer.iKeepInterfaceUp; + // + // Initialize non-zero defaults for options + // + iOptions.iHopLimit = -1; + iOptions.iMulticastHops = -1; + iOptions.iMulticastLoop = 1; + LOG(Log::Printf(_L("\t\tFlow[%u] New: protocol=%d (%d flows now)"), this, (TInt)aProtocol, iInterfacer.iFlows)); + } + +CIp6Flow::CIp6Flow(const void *aOwner, MFlowManager *aManager, CIp6Manager &aInterfacer, CFlowContext &aFlow) +: CFlowInternalContext(aOwner, aManager, aFlow), iInterfacer(aInterfacer) + { + iInterfacer.iFlows++; + iInfo.iProtocol = (TUint8)aFlow.Protocol(); + // Assumes below that the aFlow is also CIp6Flow! + iOptions = ((CIp6Flow &)aFlow).iOptions; + LOG(Log::Printf(_L("\t\tFlow[%u] New: protocol=%d cloning from Flow[%u] (%d flows now)"), + this, (TInt)iInfo.iProtocol, (TInt)&aFlow, iInterfacer.iFlows)); + } + +CIp6Flow::~CIp6Flow() + { + // + // Detach flow from a route if connected + // + if (iRoute) + iRoute->Detach(*this); + ASSERT(iInterfacer.iFlows > 0); + iInterfacer.iFlows--; + LOG(Log::Printf(_L("\t\tFlow[%u] Deleted (%d flows remaining)"), this, iInterfacer.iFlows)); + } + + +// CIp6Flow::Notify +// **************** +// +// *NOTE* The assumption here is that if the aState is negative, it +// the error state of the interface (thus the test for +// iNoInterfaceError is correct). +// +void CIp6Flow::Notify(TFlowNotifyList &aList, const TInt aState) + { + if (aState >= 0 || iInfo.iNoInterfaceError == 0) + aList.Insert(*this); + } + +// CIp6Flow::SetChanged +// ******************** +TInt CIp6Flow::SetChanged(const TInt aScope) + { + if (aScope > 0) + return iRoute ? iRoute->SetChanged(aScope-1) : 0; + iChanged = 1; + // *NOTE* It might be convenient to call SetStatus(EFlow_READY), + // if iState is PENDING (> 0) to wake up the SAP. However, this + // might cause problems, because SetStatus() may cause a destruction + // of the flow context (and possibly other structures), and if this + // is called from CIp6Route instance or higher, then all traversing + // loops would need to be protected against destruction of any object + // on the list while processing the list... -- msa + return 1; + } + +// +// CIp6Flow::Send +// ************** +TInt CIp6Flow::Send(RMBufChain& aPacket, CProtocolBase* aSrc) + /** + * Send a packet to the attached interface. + * + * @param aPacket The packet. + * @param aSrc The source (mostly ignored). + * + * @return + * @li < 0, no interface of some other missing component + * @li = 0, packet sent, but interface does not want more after this + * @li = 1, packet sent, and interface is willing to accept more + * + * The packet "ownership" is always transfered, regardless of the return type + * (the aPacket should be empty after this!) + * + * On entry, the packet must have a info structure of RMBufSendInfo. + * This routine will release the flow handle, if any attached. + */ + { + RFlowContext flow; + TInt ret = KErrNotReady; + + RMBufSendInfo *const info = RMBufSendPacket::PeekInfoInChain(aPacket); + if (info) + { + flow.Grab(info->iFlow); + ASSERT(flow.FlowContext() == this); + if (iRoute) + { + if (iSequence != iRoute->iInterface.iSequence) + { + // The valid address list has been changed since + // the last packet, verify that the current source + // address is still legal. + // *NOTE* after ReadyL processing the iHead represents + // the "upper layer view" of the addresses (for example, + // there might be home addresses loaded with mobile-ip). + // However, here the test need to be done on the ultimate + // final addresses being used for each packet, and those + // *SHOULD* *ALWAYS* be in the iStart, which is saved + // after OpenL() phase! The test here must be done to + // the final address!!! -- msa + // + ret = VerifyAddress(iStart, iRoute->iInterface); + if (ret != KErrNone) + { + // Invalid address, shutdown the flow + SetStatus(ret); + goto drop_out; + } + // Prevent further tests until next address expiration + iSequence = iRoute->iInterface.iSequence; + } + + // Because the flow has been detached from the packet, this + // is the last point where we can set the correct value for + // the KIpKeepInterfaceUp bit (which controls the counters + // for the NIF shutdown). + if (iOptions.iKeepInterfaceUp) + info->iFlags |= KIpKeepInterfaceUp; // Set + else + info->iFlags &= ~KIpKeepInterfaceUp; // Clear + ret = iRoute->Send(aPacket, aSrc, iOptions.iMulticastLoop); + } +drop_out: + flow.Close(); // <-- May delete THIS? (Probably, if above KErrInet6AddressExpired occurred!) + } + aPacket.Free(); // NOOP, if already done or assigned to elsewhere + return ret; + } + +TInt CIp6Flow::VerifyAddress(const TPacketHead &aHead, const CIp6Interface &aIf) const + /** + * Verify that the current source address is valid for the interface. + * + * For forwarding flows only, the source address scope is verified. The forwarding + * flows are allowed to use "invalid source addresses". The source address of other + * flows must be either unspecified address or a configured and assigned address on + * the interface. + * + * @return + * @li KErrNone, if valid + * @li KErrInet6SourceAddress, if address is out of scope + * @li KErrInet6AddressExpired, if address is missing. + */ + { + const TIp6Addr &src = aHead.ip6.SrcAddr(); + + // + // Verify the source address in a aHead + // + const TUint scope = (TUint)(src.Scope() - 1); + if (!TIp46Addr::Cast(src).IsUnspecified()) + { + if (scope > EScopeType_NET || aHead.iSrcId != aIf.iScope[scope]) + { + // Trying to use out of scope source address + return KErrInet6SourceAddress; + } +#ifndef WEAK_ES + // In strong ES model, don't allow sending packets + // with wrong source address (except unspecified address + // in some rare cases, and if we are forwarding data). + if (iInfo.iForwardingFlow == 0 && aIf.IsMyAddress(src) == NULL) + { + return KErrInet6AddressExpired; + } +#endif + } + return KErrNone; + } + +// +// CIp6Flow::RefreshFlow +// ********************* +void CIp6Flow::RefreshFlow() + /** + * Recompute the current flow status. + * + * This function is called when a flow in needed and the status is > 0 (pending or hold). + * This checks whether the flow can be changed into ready state, and it (re)runs the + * MIp6Hook::ReadyL phase for the hooks. + */ + { + if (iChanged) + { + LOG(Log::Printf(_L("\t\tFlow[%u] Changed, reconnect required"), this)); + return; + } + if (!iRoute) + { + iStatus = KErrNotFound; // No route available/attached! + LOG(Log::Printf(_L("\t\tFlow[%u] No route"), this)); + return; + } + + CIp6Interface &iface = iRoute->iInterface; + if (iface.iState != EFlow_READY) + { + LOG(Log::Printf(_L("\t\tFlow[%u] %S is not ready (%d)"), this, &iface.iName, iface.iState)); + iStatus = iface.iState == EFlow_HOLD ? EFlow_HOLD : EFlow_PENDING; + return; + } + + Reset(); + const TIp6Addr &dst = iHead.ip6.DstAddr(); + // For log prints, have destination address as a string. + LOG(TLogAddressPrefix log_dst(dst)); + ASSERT(iHead.iSourceSet); + iStatus = VerifyAddress(iHead, iface); + if (iStatus < 0) + return; + iSequence = iRoute->iInterface.iSequence; // Address in synch for now! + + iTimeStamp = 0; // [for why, look at comments in MoveToHolding!! -- msa] + // + // This destination will be needed, thus activate ND, if destination is not yet + // known! (note: at this point the ultimate src address of the packet is already + // known. + CIp6Route *const host_route = iRoute->iRouter ? iRoute->iRouter : iRoute; + if (host_route->iState == CIp6Route::EIncomplete) + { + LOG(Log::Printf(_L("\t\tFlow[%u] IF %u [%S] Next hop address not known, start ND [%S]"), this, iface.iScope[0], &iface.iName, &log_dst)); + host_route->StartND(iHead.ip6.SrcAddr()); + } + // + // The Path MTU is *ALWAYS* maintained to have some sensible + // value. Use it as is. + // + // 1) Check whether destination cache has a stored path mtu entry + // cachemtu == 0, if dstcache is not enabled or entry was not found in the cache + // 2) If not, use value stored with interface + const TUint cachemtu = iInterfacer.GetDstCachePathMtu(dst, iHead.iDstId); + if (cachemtu && (iPathMtu == 0 || iPathMtu > cachemtu)) + iPathMtu = cachemtu; + else if (iPathMtu == 0 || (TInt)iPathMtu > iface.iPMtu) + iPathMtu = iface.iPMtu; // Just copy current Path MTU from the interface + + if (iPathMtu > 0) + { + // Choose framing purely on whether destination is is + // IPv4 mapped (=> do IPv4) or not (=> do IPv6). + iHead.ip6.SetVersion(dst.IsV4Mapped() ? 4 : 6); + if (iHead.ip6.HopLimit() != iface.iHopLimit) + { + // The current hoplimit differs from the interface default. This + // can happen for following reasons: + // - hoplimit has not yet been initialize (still has value 0) + // - destination is multicast (the default is different) + // - hoplimit has been overriden by socket option + // - hoplimit on interface has changed due to RA + // + // The following assigns a value to hoplimit. This will get + // unnecessarily executed for each refresh, if hoplimit is + // set by socket option or destination is multicast. However, + // this should not cause too much overhead, as refresh is not + // supposed to happen frequently on every packet! + // + // Doing it in this way (comparing the limit to to interface), has + // the advantage that if RA changes the default for the interface, + // it will take effect on flows at next refresh (otherwise, RA + // processing would need to force "iChanged" on all flows to get + // it effective) -- still may have to do it that way, but first + // this solution is tested if it will be sufficient -- msa + // + if (TIp46Addr::Cast(dst).IsMulticast()) + iHead.ip6.SetHopLimit(iOptions.iMulticastHops < 0 ? 1 : iOptions.iMulticastHops); + else if (iOptions.iHopLimit >= 0) // override by socket option? + iHead.ip6.SetHopLimit(iOptions.iHopLimit); +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + // According to RFC 4861, Echo Reply needs to respond with CurHopLimit set earlier by RA + //else if (TIp46Addr::Cast(dst).IsLinkLocal() && iface.Interfacer().iLinkLocalTTL >= 0) + //iHead.ip6.SetHopLimit(iface.Interfacer().iLinkLocalTTL); +#else + else if (TIp46Addr::Cast(dst).IsLinkLocal() && iface.Interfacer().iLinkLocalTTL >= 0) + iHead.ip6.SetHopLimit(iface.Interfacer().iLinkLocalTTL); +#endif //SYMBIAN_TCPIPDHCP_UPDATE + else + iHead.ip6.SetHopLimit(iface.iHopLimit); + } + // + // Run the ReadyL phase for the flow + // + + RefreshHooks(); // Set status from hooks + iMgr->FlowStartRefresh(*this); // (this needs be done for inner header, if tunnels!) + + // + // If we already have a packet, we should make it possible to delay this. + // EFlow_PENDING is only meant for NIF startup and EFlow_HOLD for temporarily blokcs. + // Thus, for future this should something like EFlow_ROUTEHOLD = 3 + // but to minimize regression with current hooks we use HOLD for now. + // + if (!iRoute->iPacket.IsEmpty()) + SetStatus(EFlow_HOLD); + // + // If the upper layer has not specified the source address + // explicitly (iLocalSet TRUE), then the current source address + // from the iHead.ip6.SrcAddr() is copied for the upper layer. + // This is done *after* the hooks so that they have a chance to + // change it (for example, mobile IP with Care/Home Address). + // + // If a hook adds tunnel(s), it is the responsiblity of the hook + // to change the source in iHead to the inner source while + // doing the ReadyL() processing (upper layer will see the + // inner source!) + // + if (!iInfo.iLocalSet) + iInfo.iLocal.SetAddress(iHead.ip6.SrcAddr()); + // + // Final scope id's are unconditionally set + // (for now) + iInfo.iLocal.SetScope(iHead.iSrcId); + iInfo.iRemote.SetScope(iHead.iDstId); + } + else + { + iStatus = KErrInet6NoPathMtu; + LOG(Log::Printf(_L("\t\tFlow[%u] IF %u [%S] has no MTU"), this, iface.iScope[0], &iface.iName)); + } + } + + +#ifdef SYMBIAN_TCPIPDHCP_UPDATE +// CIp6Flow::IsNdPacketPendingResolution ( RFC 4861 ) +// Returns ETrue if empty else returns EFalse +// ********************* +TBool CIp6Flow::IsNdPacketPendingResolution() + { + // CIP6Route Class holds information related to pending Neighbour Discovery packet waiting for Address resolution on that route + // This method returns ETrue if there are no pending packets + TBool ndPktExists = EFalse; + if (iRoute) //If Route Exists + { + if(!iRoute->iPacket.IsEmpty()) // Pending ND Packet exists + { + ndPktExists = ETrue; + return ndPktExists; + } + else // Pending ND Packet doesnt exist + { + ndPktExists = EFalse; + return ndPktExists; + } + } + return ndPktExists; + } +#endif //SYMBIAN_TCPIPDHCP_UPDATE + + +// CIp6Flow::RouteFlow +// ******************* +TInt CIp6Flow::RouteFlow(TPacketHead &aHead) + /** + * Route a flow to an interface based on a TPacketHead + * + * Using the current connection parameters in the given TPacketHead (aHead), + * try to locate an interface for the flow. If found, complete TPacketHead + * information from the selected interface. Verify the validity of the + * source address, if it is defined (TPacketHead::iSourceSet == 1), or try + * to select the source address otherwise. + * + * In addition to initial call in Connect(), the RouteFlow is called + * after each MIp6Hook::OpenL, if any the addressing information has been + * changed by the hook. + * + * @param aHead The connection parameters. + * + * @return A value for the flow status, as follows: + * @li EFlow_READY, if routing succeeded and route attached + * @li EFlow_PENDING, if route or source address cannot be selected + * @li KErrInet6NoDestination, if destination address is missing + * @li KErrInet6SourceAddress, if source address is out of scope + * @li KErrInet6AddressExpired, if source address is not valid for the interface. + * @li KErrInet6NoRoute, if route or source address cannot be selected and on demand setup is not desired. + * + * In case the route is not found, the function starts a neighbor probe for the + * destination address on all interfaces in the current scope (only if this + * feature is enabled via the configuration parameter or one or more interfaces + * have link local source addresses). + * + */ + { + const TIp6Addr &dst = aHead.ip6.DstAddr(); + TUint dstType = (TUint)(dst.Scope()-1); + if (dst.IsUnspecified() || dstType > EScopeType_NET) + return KErrInet6NoDestination; + + CIp6Route *rt = NULL; + for (;;) /* NOT A LOOP, JUST FOR BREAK EXITS */ + { + // For "forwarding flows", the source address is set, but must not affect the + // route selection (because it would fail due to address not being a valid + // address for this host!). Need two temp locations to hold the source info. + const TInt use_source = aHead.iSourceSet && iInfo.iForwardingFlow == 0; + const TIp6Addr &src_tmp = use_source ? aHead.ip6.SrcAddr() : KInet6AddrNone; + const TUint32 src_id = use_source ? aHead.iSrcId : 0; + + if (aHead.iDstId == 0) + { + // Application has not specified scope. Find route within the locked scope. + rt = iInterfacer.FindRoute(dst, iOptions.iLockId, iOptions.iLockType, src_tmp, src_id); + if (rt != NULL) + { + aHead.iDstId = rt->iInterface.iScope[dstType]; + break; // --> Success, route found + } + // Special kludge, because the socket server locks unnecessarily to IAP level + // by default: if the locking is to IAP, then try finding an alternate route + // by the network id only. + if (iOptions.iLockType == EScopeType_IAP) + { + CIp6Interface *const ifp = iInterfacer.FindInterface(iOptions.iLockId, (TScopeType)iOptions.iLockType); + if (ifp != NULL) + { + // At least one interface with matching IAP exists, try to find a route using the + // network scope id from this interface. + rt = iInterfacer.FindRoute(dst, ifp->iScope[EScopeType_NET], EScopeType_NET, src_tmp, src_id); + if (rt != NULL) + { + aHead.iDstId = rt->iInterface.iScope[dstType]; + break; // --> Success, route found + } + // should probing scope be also widened to network? + } + } + // For probing, limit it to locked scope, if any + dstType = iOptions.iLockType; + aHead.iDstId = iOptions.iLockId; + } + else + { + // If there is a locking to smaller scope than the requested destination scope, + // then try to find a route within the locked scope. + if (iOptions.iLockId && dstType > iOptions.iLockType) + { + rt = iInterfacer.FindRoute(dst, iOptions.iLockId, iOptions.iLockType, src_tmp, src_id); + if (rt != NULL && rt->iInterface.iScope[dstType] == aHead.iDstId) + break; // --> Success, route found + } + // If that route is not found or if the found interface does not match the required + // destination scope, then assume application wants to ignore the locking scope => search + // route by destination scope only. + rt = iInterfacer.FindRoute(dst, aHead.iDstId, dstType, src_tmp, src_id); + if (rt != NULL) + break; // --> Success, route found + } + // + // No route found, do the probing if enabled and return no route. + // + iInterfacer.ProbeDestination(dst, aHead.iDstId, dstType, src_tmp, src_id); + return iInfo.iNoInterfaceUp ? KErrInet6NoRoute : EFlow_PENDING; // --> Fail, route not found or pending + } + // + // The route (and interface) has been located + // + rt->Attach(*this); + + // Lock is applied only once (must be explicitly re-enabled, if new lock is to be applied) + iOptions.iLockId = 0; + const CIp6Interface &iface = rt->iInterface; + + aHead.iInterfaceIndex = iface.iScope[0]; + // + TIp6Addr &src = aHead.ip6.SrcAddr(); + // If source address is already specified, then skip the + // automatic address selection. TPacketHead::iSourceSet is + // initialized from the upper layer iLocalSet, and can be + // modified by the hooks in OpenL phase. + if (aHead.iSourceSet) + { + if (aHead.iSrcId == 0) + aHead.iSrcId = iface.iScope[(src.Scope()-1)&0xF]; + return VerifyAddress(aHead, iface); + } + else if (iface.SelectSource(src, dst) == NULL) + { + const TIp6AddressInfo* address = iface.FindIpv4LinkLocalAddr(); + + // If no appropriate routable address exists, fallback to any link local + // address regardless of prefix (this is required by the ZEROCONF RFC and + // a link local address can be used to reach any neighbour on-link using + // ARP). + if( address && address->IsAssigned() ) + { + #ifdef _LOG + TInetAddr addr; + addr.SetAddress( address->iId ); + TBuf<39> addrStr; + addr.Output( addrStr ); + + Log::Printf(_L("CIp6Flow::RouteFlow - Unable to select source address for flow 0x%X based on destination - defaulting to link local address %S"), this, &addrStr); + #endif + + src = address->iId; + } + else + { + return EFlow_PENDING; + } + } + // + // Fix source address and scope + // + aHead.iSourceSet = 1; + aHead.iSrcId = iface.iScope[(src.Scope()-1)&0xF]; + return EFlow_READY; + } + + + +// +// CIp6Flow::Connect +// ***************** +void CIp6Flow::Connect() + /** + * Attach a flow to route and interface. + * + * At the start, iHead will contain the parameters affecting the + * flow connection (addresses, ports, etc.) in IPv6 variant of the + * iHead. + * + * Runs the "Open Phase" of flow hook mechanism for this flow. This + * will attach the interested outbound flow hooks to this flow. + */ + { + + // Run the open phase +retry_connect: + // Clean up all previous crud + iStatus = EFlow_READY; // Make sure Disconnect doesn't call the caller back! + Disconnect(); + iChanged = 0; + // + // Initialize the flow iHead (TPacketHead) from the upper layer information + // + const TInetAddr & localAddr = LocalAddr(); + const TInetAddr & remoteAddr = RemoteAddr(); + iHead.ip6.Init(); // Set Version=6, hoplimit=0 + iHead.ip6.SetVersion(0); // We really don't know yet! + iHead.ip6.SetSrcAddr(localAddr.Ip6Address()); + iHead.ip6.SetDstAddr(remoteAddr.Ip6Address()); + // Load Selector Fields + iHead.iProtocol = (TUint8)Protocol(); + iHead.iSrcPort = (TUint16)LocalPort(); + iHead.iDstPort = (TUint16)RemotePort(); + GetIcmpTypeCode(iHead.iIcmpType, iHead.iIcmpCode); + // + iHead.ip6.SetNextHeader(iHead.iProtocol); + + iHead.ip6.SetTrafficClass(iOptions.iTrafficClass); + iHead.ip6.SetFlowLabel(remoteAddr.FlowLabel()); + + iHead.iSrcId = localAddr.Scope(); + iHead.iSourceSet = iInfo.iLocalSet; + iHead.iDstId = remoteAddr.Scope(); + + // Initialize locking (if any) + iOptions.iLockId = iInfo.iLockId; + iOptions.iLockType = iInfo.iLockType; + +#ifdef SYMBIAN_NETWORKING_UPS + TBool isScoped = EFalse; +#endif //SYMBIAN_NETWORKING_UPS + + for (;;) /* JUST FOR BREAK-EXITS, NOT REALLY A LOOP! */ + { +#ifdef SYMBIAN_NETWORKING_UPS + // User Prompt Service (UPS) support. + // + // Generate NoBearer() upcalls on all flows that have an upper provider and have not + // yet had the scope set by ESOCK. Generate NoBearer() even if enough information + // is available to route them with a straight Bearer() upcall. This is to allow + // ESOCK to apply additional authorisation on any usage of a socket by a process. + // + // The check against upper provider is to exempt internal flows that aren't associated + // with an ESOCK socket - for example, those used for Neighbour Discovery or ICMP. + // + + TBool upsPromptingPossible = UPSPromptingPossible(); + if (upsPromptingPossible) + { + if ((iOptions.iLockId == 0 && (HasProvider() || iHead.iDstId == 0)) || iUpsAuthorisationRequired) + { + // + // Accept only unambiguous loopback addresses if scope is not locked. + // + + // + // iUpsAuthorisationRequired is True if iOptions.iLockId has been set by + // SetOption KSoInterfaceIndex. This allows a gratituous (scoped) nobearer + // up call to be made which results in a request being made to the Ups Server. + // + const TInt scope_level = iHead.ip6.DstAddr().Scope(); + + if (scope_level != KIp6AddrScopeNodeLocal) // not a loopback address + { + // TODO 1116 NW: find out the correct means of preventing NoBearer() on + // destination addresses which are one of our interface addresses. + // Pity this is going to take up cpu time for the check. + // + // iInterfacer.IsForMeAddress(iHead.ip6.DstAddr(), 0) seems inappropriate + // as it doesn't do scope matching and works with interface index second argument, + // which is meaningless in this context. + // + // iInterfacer.FindInterface(RemoteAddr()) seems more appropriate, but not + // entirely complete as it doesn't do ELoopback route matching. So it will match + // addresses. If the address of an interface doesn't quite match, but it has a + // route does match, then it won't pick tis up. Perhaps it is good enough. Perhaps + // we don't need to do anything here at all! + + // Sending to a local interface address is treated as a loopback, so avoid issuing + // NoBearer() in this case - drop through to Bearer(). + + // The following 2 lines have been removed as iInterfacer does not always return the correct interface + // const CIp6Interface* ifp = iInterfacer.FindInterface(RemoteAddr()); + // if (ifp == NULL) + + { + if (iHead.iDstId != 0 || scope_level == KIp6AddrScopeGlobal) + { + // If we *could* have gone through and attempted to connect the flow, + // were it not for UPS, then mark this with a flag which will be + // communicated to ESock in the NoBearer() upcall. Effectively, we + // are issuing what we think is a gratuitous NoBearer() just for ESock + // to get a look in before the flow is connected. ESock will + // issue a SetOption(KSoConnectionInfo) with NetworkId KNetworkIdFromAddress + // instead of what it thinks is the correct NetworkId (which is not going to + // be based on the scope from the user). + isScoped = ETrue; + } + break; + } + } + } + + // ESock can issue a SetOption(KSoConnectionInfo) with NetworkId KNetworkIdFromAddress after + // NoBearer() on flows where either the scope id has been set by the user, or the address is + // unambiguous. This indicates that the flow is now able to be connected using the scope specified, + // rather than ESock giving the scope explicitly. In effect, ESock is saying that it has "blessed" + // the flow for connection. In other words, "attempt to attach the flow to a route, but determine + // the NetworkId from the socket address, as you've already indicated in the NoBearer() that you + // have enough information to do so". + // + // KNetworkIdFromAddress is just a magic way of signalling this method, as issuing a KSoConnectionInfo + // with NetworkId == 0 is filtered out at a higher level. Reset iLockId to zero as we've received + // the signal that we need. + // + + if (iOptions.iLockId == KNetworkIdFromAddress) + { + iOptions.iLockId = 0; + } + } + else + { + if (iHead.iDstId == 0 && iOptions.iLockId == 0) + { + // + // Accept only unambiguous addresses if scope is not locked. -MikaL + // + const TInt scope_level = iHead.ip6.DstAddr().Scope(); + if (scope_level != KIp6AddrScopeGlobal && // not IPv6 global address + scope_level != KIp6AddrScopeNodeLocal) // not a loopback address + break; + } + } +#else + if (iHead.iDstId == 0 && iOptions.iLockId == 0) + { + // + // Accept only unambiguous addresses if scope is not locked. -MikaL + // + const TInt scope_level = iHead.ip6.DstAddr().Scope(); + if (scope_level != KIp6AddrScopeGlobal && // not IPv6 global address + scope_level != KIp6AddrScopeNodeLocal) // not a loopback address + break; + } +#endif + + iStatus = RouteFlow(iHead); + if (iStatus != 0) + break; + iHdrSize = 0; + iStatus = iMgr->FlowSetupHooks(*this); + + if (iStatus != 0) + break; // Pending or fatal state, cannot proceed. + +#ifndef SYMBIAN_NETWORKING_UPS + // Decide on what destinations require network services capabily: + // - if it is my own assigned address allow without capability (IsMyPrefix test) + // - otherwise, allow only node local destinations (loopbacks) + if (!ApplyStaticSecurityCheck()) + { + return; + } +#else + // Decide on what destinations require network services capabily: + // - if it is my own assigned address allow without capability (IsMyPrefix test) + // - otherwise, allow only node local destinations (loopbacks) + if (!upsPromptingPossible) + { + if (!ApplyStaticSecurityCheck()) + { + return; + } + } + +#endif + // + // The final interface has been located + // + Start(); + SelectNextHop(); + if (iStatus != 0) + return; + ASSERT(iRoute != NULL); +#ifdef _LOG + { + TLogAddressPrefix src(iInfo.iLocal); + TLogAddressPrefix dst(iInfo.iRemote); + Log::Printf(_L("\t\tFlow[%u] Connect: prot=%d src=[%S], dst=[%S] attached to IF %u [%S]"), + this, (TInt)iInfo.iProtocol, &src, &dst, iRoute->iInterface.iScope[0], &iRoute->iInterface.iName); + } +#endif + TPckgBuf netinfo; + netinfo().iIAPId = iRoute->iInterface.iScope[EScopeType_IAP]; + netinfo().iNetworkId = iRoute->iInterface.iScope[EScopeType_NET]; + Bearer(netinfo); + RefreshFlow(); + return; + } + // + // Cannot locate any interface for the flow. + // Punt the flow directly into holding state + // + iInterfacer.MoveToHolding(*this); + ASSERT(iRoute != NULL); +#ifdef _LOG + { + TLogAddressPrefix src(iInfo.iLocal); + TLogAddressPrefix dst(iInfo.iRemote); + Log::Printf(_L("\t\tFlow[%u] Connect: prot=%d src=[%S], dst=[%S] into holding (%d)"), + this, (TInt)iInfo.iProtocol, &src, &dst, iStatus); + } +#endif + if (iStatus > 0) + { + // The next hop could not be selected because no suitable + // interface or route matched the requirements. Need to + // activate new interfaces (or just keep waiting for them) + + if (iInfo.iNoInterfaceUp == 0) + { + // Because NoBearer may cause immediate call to SetOption with + // connection info, there are the following problems: + // + // 1) The connection info SetOption should wake up the flow, and + // needs to call SetStatus(EFlow_READY). If flow is pending, + // it will cause a CanSend. This must be prevented => Thus, + // for NoBearer call, iStatus is temporarily set to READY. + // 2) Must detect whether NoBearer called SetOption, and if it + // did, this code must retry the connect. => Thus, remember + // current locking and see if it has changed during the + // NoBearer. + // + const TInt old_status = iStatus; + const TUint old_type = iInfo.iLockType; + const TUint32 old_id = iInfo.iLockId; + iStatus = EFlow_READY; + + _LIT8(KProtoIPv6, "protocol=ip6"); + _LIT8(KProtoIPv4, "protocol=ip"); + +#ifdef SYMBIAN_NETWORKING_UPS + const TInt KMaxArgLen = 19; // sufficient for longest string length ("protocol=ip6 scoped") + TBuf8 arg(iHead.ip6.DstAddr().IsV4Mapped() ? KProtoIPv4() : KProtoIPv6()); + if (isScoped) + { + _LIT8(KScoped, " scoped"); + arg.Append(KScoped()); + } + + if (iUpsAuthorisationRequired) + { + // A NoBearer upcall is being made after a set option KSoInterfaceIndex + // which will result in a request to the UPS Server. + // Set a flag to indicate that a UPS request is pending. + // This flag will be reset when a KSoConnectionInfo is received in + // response to the NoBearer upcall. + iUpsAuthorisationPending = ETrue; + } + NoBearer(arg); +#else + NoBearer(iHead.ip6.DstAddr().IsV4Mapped() ? KProtoIPv4() : KProtoIPv6()); +#endif + if (iInfo.iLockType != old_type || iInfo.iLockId != old_id) + goto retry_connect; + // + // NoBearer didn't call or change locking, restore + // original pending status and exit. + // + iStatus = old_status; + } + } + } + +#ifdef SYMBIAN_NETWORKING_UPS +TBool CIp6Flow::UPSPromptingPossible() + { + TBool upsPromptingPossible = EFalse; + + if (HasProvider()) + { + TUint version = 0; + _LIT8(KProviderBindings, "MProviderBindings"); + MProviderBindings* providerBindings = 0; + TRAPD(err, providerBindings = (MProviderBindings*) GetProviderApiL(KProviderBindings, &version)); + if ((providerBindings != NULL) && (err == KErrNone )) + { + upsPromptingPossible = providerBindings->HasSocket(); + } + } + return upsPromptingPossible; + } +#endif + +TBool CIp6Flow::ApplyStaticSecurityCheck() + { + TBool rc = ETrue; + + ASSERT(iRoute); + if (!iRoute->IsMyPrefix() && iHead.ip6.DstAddr().Scope() > KIp6AddrScopeNodeLocal) + { + iStatus = CheckPolicy(KPolicyNetworkServices, "TCPIP Connect"); + if (iStatus != 0) + rc = EFalse; + } + return rc; + } +// CIp6Flow::Disconnect +// ******************** +void CIp6Flow::Disconnect() + /** + * Disconnect flow (remove hooks). + */ + { + iChanged = 1; + iHead.iPacket.Free(); + RemoveHooks(); + // + // Setting EFlow_READY should cause a wakeup for the + // SAP, if the flow status is pending. + // *WARNING* As Disconnect() is called in various + // contexts, there is a danger for infinite + // recursion, if this is called carelessly for + // PENDING flows... -- msa + SetStatus(EFlow_READY); + } + +// +// CIp6Flow::InterfaceSMtu/InterfaceRMtu +// ************************************* +// Return interface semd MTUs, if connected. +TInt CIp6Flow::InterfaceSMtu() const + { + return iRoute ? iRoute->iInterface.iSMtu : KErrNotReady; + } + +// Return interface receive MTUs, if connected. +TInt CIp6Flow::InterfaceRMtu() const + { + return iRoute ? iRoute->iInterface.iRMtu : KErrNotReady; + } + +// +// Local utility functions +// +static TInt GetIntValue(const TDesC8 &aOption, TInt aDefault) + { + if (aOption.Length() < (TInt)sizeof(TInt)) + return aDefault; + // note: here it is assumed that the Ptr() is properly + // aligned, but for debug check it! -- msa) + ASSERT((((TUint)aOption.Ptr()) & 0x3) == 0); + return *((TInt *)aOption.Ptr()); + } + +static void SetIntValue(TDes8 &aOption, TInt aValue) + { + aOption = TPtrC8((TUint8 *)&aValue, sizeof(aValue)); + } +// +// CIp6Flow::GetOption +// ******************* +TInt CIp6Flow::GetOption(TUint aLevel, TUint aName, TDes8 &aOption) const + { + if (aLevel == KSolInetIp) + { + switch(aName) + { + case KSoIpTOS: + SetIntValue(aOption, iOptions.iTrafficClass); + return KErrNone; + case KSoIpEcn: + SetIntValue(aOption, iOptions.iTrafficClass & 3); + return KErrNone; + case KSoIpTTL: // Old IPv4 option + case KSoIp6UnicastHops: // New IPv6/4 option + SetIntValue(aOption, + // ...return explicitly set value, if defined by socket option + iOptions.iHopLimit >= 0 ? iOptions.iHopLimit : + // ...return current value from interface, if connected flow + iRoute ? iRoute->iInterface.iHopLimit : + // ...return system default otherwise + iInterfacer.iMaxTTL); + return KErrNone; + case KSoIp6InterfaceUnicastHops: // ignores any socket option which would override the current setting + SetIntValue(aOption, + // ...return current value from interface, if connected flow + iRoute ? iRoute->iInterface.iHopLimit : + // ...return system default otherwise + iInterfacer.iMaxTTL); + return KErrNone; + case KSoIp6MulticastHops: + // If no socket specific option has been set, then return the default 1. + SetIntValue(aOption, iOptions.iMulticastHops < 0 ? 1 : iOptions.iMulticastHops); + return KErrNone; + case KSoIp6MulticastLoop: + SetIntValue(aOption, iOptions.iMulticastLoop); + return KErrNone; + case KSoNoInterfaceError: + SetIntValue(aOption, iInfo.iNoInterfaceError); + return KErrNone; + case KSoInterfaceIndex: + SetIntValue(aOption, (TInt)( + (iInfo.iLockType == 0) ? iInfo.iLockId : + (iRoute && !iRoute->IsHoldingRoute()) ? iRoute->iInterface.iScope[0] : + 0)); + return KErrNone; + case KSoKeepInterfaceUp: + SetIntValue(aOption, iOptions.iKeepInterfaceUp); + return KErrNone; + case KSoNextHop: + // Return information about the current route/next hop selection. + if (aOption.Length() != sizeof(TInetRouteInfo)) + return KErrArgument; + if (iStatus == EFlow_READY && iRoute) + { + TInetRouteInfo &opt = *(TInetRouteInfo*)aOption.Ptr(); + TTime stamp; + stamp.UniversalTime(); + const CIp6Route *route = iRoute->iRouter ? iRoute->iRouter : iRoute; + route->FillRouteInfo(opt, route->iInterface.Elapsed(stamp)); + return KErrNone; + } + return KErrNotReady; // The flow is not connected. + default: + break; + } + } + return iMgr->GetFlowOption(aLevel, aName, aOption, *this); + } +// CIp6Flow::SetOption +// ******************* +TInt CIp6Flow::SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption) + { + if (aLevel == KSolInetIp) + { + // ...so far all implemented options use only int + // parameter. Just save code space and prefetch value, + // whether it is needed or not (GetIntValue should be safe + // to call in all cases). + const TInt val = GetIntValue(aOption, 0); + switch (aName) + { + // IPv4 TOS and IPv6 TrafficClass fields are now split to the TOS part and + // to the Explicit Congestion Notification (ECN) part (the least significant two + // bits). TCP SAP may prevent modifying the ECN bits with the TOS option. With UDP + // the option works in the traditional way. + case KSoIpTOS: + if (val < 0 || val > 255) + return KErrArgument; // Invalid option value + + if (val != iOptions.iTrafficClass) + { + iOptions.iTrafficClass = (TUint8)val; + iChanged = 1; + } + return KErrNone; + case KSoIpEcn: + if (val < 0 || val > 3) + return KErrArgument; + if (val != (iOptions.iTrafficClass & 3)) + { + TUint8 tosfield = (TUint8)val; + tosfield |= iOptions.iTrafficClass & 0xfc; + iOptions.iTrafficClass = tosfield; + iChanged = 1; + } + return KErrNone; + case KSoIpTTL: // Old IPv4 option + case KSoIp6UnicastHops: // New IPv6/4 option + if (val < -1 || val > 255) + return KErrArgument; + if (val != iOptions.iHopLimit) + { + iOptions.iHopLimit = (TInt16)val; + iChanged = 1; + } + return KErrNone; + case KSoIp6MulticastHops: + if (val < -1 || val > 255) + return KErrArgument; + if (val != iOptions.iMulticastHops) + { + iOptions.iMulticastHops = (TInt16)val; + iChanged = 1; + } + return KErrNone; + case KSoIp6MulticastLoop: + // For now, assume this option doesn't require iChanged processing! + if (val == 0) + iOptions.iMulticastLoop = 0; // disable loopback of multicast + else if (val == 1) + iOptions.iMulticastLoop = 1; // enable loopback of multicast + else + return KErrArgument; + //break; + return KErrNone; // was 'break'? why? fixed to return 9.1.2002/msa + case KSoNoInterfaceError: + // Control whether flow gets interface errors or not + if (val == 0) + iInfo.iNoInterfaceError = 0; + else if (val == 1) + iInfo.iNoInterfaceError = 1; + else + return KErrArgument; + return KErrNone; + case KSoInterfaceIndex: + // Fix flow to a specific interface, if non-zero + if (iInfo.iLockType != 0 || iInfo.iLockId != (TUint)val) + { + iInfo.iLockType = EScopeType_IF; + iInfo.iLockId = (TUint)val; + iChanged = 1; +#ifdef SYMBIAN_NETWORKING_UPS + // + // iInfo.iLockId == 0 is tested as a condition which allows + // entry into the logical branch in CIp6Flow::Connect() that + // triggers a NoBearer up call. A TNoBearer message results in + // a call to the UPS Server. Setting iUpsAuthorisationRequired + // ensures this branch is executed. + // + iUpsAuthorisationRequired = ETrue; +#endif + } + return KErrNone; + case KSoKeepInterfaceUp: + // Just treat any non-zero val as "1" + if ((int)iOptions.iKeepInterfaceUp == (val != 0)) + return KErrNone; // Value not changed + iOptions.iKeepInterfaceUp = (val != 0); + // Update the flow count on the interface + if (iRoute) + iRoute->iInterface.UpdateFlowCount((val != 0) ? 1 : -1); + return KErrNone; + case KSoNoSourceAddressSelect: + iInfo.iLocalSet = 1; + return KErrNone; + case KSoNextHop: + // Force next hop selection. The option argument is ignored. + Start(); + SelectNextHop(); + return iStatus <= 0 ? iStatus : KErrNotReady; + default: + break; + } + } + else if (aLevel == STATIC_CAST(TUint, KSOLProvider)) + { + const TUint optlen = aOption.Length(); + const TUint8 *optptr = aOption.Ptr(); + + if (aName == (TUint)KSoConnectionInfo) + { + if (optlen >= sizeof(TSoIfConnectionInfo)) + { + const TSoIfConnectionInfo &opt = *(TSoIfConnectionInfo*)optptr; + TUint new_type; + TUint new_lock; + if (opt.iIAPId) + { + // Locking on IAP + new_type = EScopeType_IAP; + new_lock = opt.iIAPId; + } + else + { + // Locking on NetworkId + new_type = EScopeType_NET; + new_lock = opt.iNetworkId; + } + if (iInfo.iLockId != 0 && iInfo.iLockType == EScopeType_IF) + { + // If the flow has already been explicitly locked to an interface + // using the socket option KSoInterfaceIndex, then assume application + // really wants to override the default connection settings that would + // be coming from the socket server. + LOG(Log::Printf(_L("\t\tFlow[%u] Set ConnectionInfo: IF=%d already interface locked, %S=%d ignored"), + this, (TInt)iInfo.iLockId, &LogScopeName(new_type), (TInt)new_lock)); + +#ifdef SYMBIAN_NETWORKING_UPS + if (iUpsAuthorisationRequired && iUpsAuthorisationPending) + { + // This branch is called as a direct result of a NoBearer call which has + // resulted from CIp6Flow::Connect() being called after SetOption(KSoInterfaceIndex). + // Now reset the flags which allowed a NoBearer upcall to be made. + + iUpsAuthorisationRequired = EFalse; + iUpsAuthorisationPending = EFalse; + + // The following three lines cater for the following stack trace: + // CIp6Flow::Connect() + // NoBearer("scoped") + // SetOption(KSoConnectionInfo(KNetworkIdFromAddress)) + // When we eventually return to CIp6Flow::Connect(), we need to ensure that it + // retries the flow connect. + if (new_type == EScopeType_NET && new_lock == KNetworkIdFromAddress) + { + iInfo.iLockType = new_type; + iInfo.iLockId = new_lock; + } + + SetStatus(EFlow_READY); + } +#endif + + return KErrNone; + } + LOG(Log::Printf(_L("\t\tFlow[%u] Set ConnectionInfo: %S=%d -> %S=%d"), + this, &LogScopeName(iInfo.iLockType), (TInt)iInfo.iLockId, &LogScopeName(new_type), (TInt)new_lock)); + if (iInfo.iLockType != new_type || iInfo.iLockId != new_lock) + { + iInfo.iLockType = new_type; + iInfo.iLockId = new_lock; + iChanged = 1; + // Wake up the flow, if pending + SetStatus(EFlow_READY); + } + return KErrNone; + } + LOG(Log::Printf(_L("\t\tFlow[%u] Set ConnectionInfo: Bad TSoIfConnection"), this)); + return KErrArgument; + } + } + return iMgr->SetFlowOption(aLevel, aName, aOption, *this); + } + +// +// ************************ +// CIp6Route Implementation +// ************************ +// +// +// CIp6Route +// ********* +// Construct & Destruct +// +CIp6Route::CIp6Route(TUint aIndex, CIp6Manager &aMgr, const TIp6Addr &aAddr, TInt aPrefix, CIp6Interface &aInterface) +: iIndex(aIndex), + iInterfacer(aMgr), + iPrefix(aAddr), + iLength((TUint8)aPrefix), + iMetric(KPreferenceMetric[ERoutePreference_MEDIUM]), + iTimeout(CIp6RouteTimeoutLinkage::Timeout), + iInterface(aInterface) + { + } + +CIp6Route::~CIp6Route() + { + LOG(Log::Write(_L("~CIp6Route()"))); + CancelTimer(); // ..if any pending + iPacket.Free(); // (if any) + // + // Just detach all flows attached to this route + // (Should have a "route down" error status? -- msa) + // + TInt changed = 0; + CIp6Flow *f; + while ((f = iFlowList) != NULL) + { + iFlowList = f->iNext; + f->iRoute = NULL; + f->iNext = NULL; + f->SetStatus(EFlow_DOWN); + changed -= f->iOptions.iKeepInterfaceUp; + } + iInterface.UpdateFlowCount(changed); + } + +// +// CIp6Route::Attach +// ***************** +/** +// Attach flow to a specific route (it has already +// been decided elsewhere that this route is the +// correct one; this function is just pure housekeeping) +*/ +void CIp6Route::Attach(CIp6Flow &aFlow) + { + // + // A flow can only be attached to one route at time. + // If different from this, then detach first. + // + if (aFlow.iRoute != this) + { + if (aFlow.iRoute) + aFlow.iRoute->Detach(aFlow); + // + // Add to the flow list of the route + // + aFlow.iRoute = this; + aFlow.iNext = iFlowList; + iFlowList = &aFlow; + iInterface.UpdateFlowCount(aFlow.iOptions.iKeepInterfaceUp); + } + } + +#ifdef _LOG +const TDesC &CIp6Route::LogRouteType() const + { + _LIT(KIncomplete, "Incomplete"); + _LIT(KReachable, "Reachable"); + _LIT(KStale, "Stale"); + _LIT(KDelay, "Delay"); + _LIT(KProbe, "Probe"); + + _LIT(KMyPrefix, "MyPrefix"); + _LIT(KOnlink, "Onlink"); + _LIT(KGateway, "Gateway"); + _LIT(KAnycast, "Anycast"); + _LIT(KRedirect, "Redirect"); + _LIT(KMulticast, "Multicast"); + _LIT(KHolding, "Holding"); + _LIT(KInvalid, "Invalid"); + + switch (iState) + { + case EIncomplete: return KIncomplete; + case ELoopback: if (iIsMulticast) return KMulticast; else return KMyPrefix; + case EOnlink: return KOnlink; + case EGateway: return KGateway; + case ERedirect: return KRedirect; + case EAnycast: return KAnycast; + case EReachable: return KReachable; + case EStale: return KStale; + case EDelay: return KDelay; + case EProbe: return KProbe; + case EHolding: return KHolding; + default: break; + } + return KInvalid; + } + +void CIp6Route::LogRoute(const TLifetime aLifetime) const + { + // index interface route/prefix type address ... + _LIT(KFormat0, "\tIF %u [%S] ROUTE %u %S %S %S metric=%d"); + _LIT(KFormat1, "\tIF %u [%S] ROUTE %u %S %S %S metric=%d LT=%u"); + _LIT(KFormat2, "\tIF %u [%S] ROUTE %u %S %S %S metric=%d REMOVED"); + + const TDesC &format = + aLifetime == KLifetimeForever ? KFormat0() : aLifetime != 0 ? KFormat1() : KFormat2(); + + TLogAddressPrefix tmp(iPrefix, iLength); + TLogAddressPrefix gw; + if (Type() != CIp6Route::ELoopback) + { + TInetAddr addr; + iAddress.GetAddress(addr); + gw.Set(addr); + } + Log::Printf(format, + iInterface.iScope[0], + &iInterface.iName, + iIndex, + &tmp, + &LogRouteType(), + &gw, + (TInt)iMetric, + aLifetime); + } +#endif +// +// CIp6Route::Attach +// ***************** +/** +// Detach all flows from one route and add them to this route +// +// @li *NOTE* +// As flows are set into PENDING state, this makes only +// sense as a method of the "holding route". +*/ +void CIp6Route::Attach(CIp6Route &aRoute) + { + CIp6Flow *f; + TInt count = 0; + + while ((f = aRoute.iFlowList) != NULL) + { + aRoute.iFlowList = f->iNext; + f->iNext = iFlowList; + iFlowList = f; + f->iRoute = this; + // reminder: SetStatus for pending (> 0) will not change the + // flow status, if it already is in error state (the error + // state is preserved) -- msa + f->SetStatus(EFlow_PENDING); + //++count; + count += f->iOptions.iKeepInterfaceUp; + } + iInterface.UpdateFlowCount(count); + aRoute.iInterface.UpdateFlowCount(-count); + } + +// +// CIp6Route::Detach +// ***************** +// Disconnect flow from route. Just pure house keeping +void CIp6Route::Detach(CIp6Flow &aFlow) + { + if (this == aFlow.iRoute) + { + CIp6Flow **h, *f; + // + // Some concern: Will this list normally be short enough for + // this simple scan or should a double linked list with + // deque applied? -- msa + // + aFlow.iRoute = NULL; + for (h = &iFlowList; (f = *h) != NULL; h = &(f->iNext)) + if (f == &aFlow) + { + *h = f->iNext; + iInterface.UpdateFlowCount(-(int)aFlow.iOptions.iKeepInterfaceUp); + return; // Succesful/Normal result. + } + User::Panic(_L("DEBUG"), 0); // Should never happen! + } + else + { + ASSERT(aFlow.iRoute == NULL); + } + } + +// +// CIp6Route::NofifyFlows +// ********************** +// Notify all flows attached to this route +void CIp6Route::NotifyFlows(TInt aState) + { + TFlowNotifyList list; + for (CIp6Flow *f = iFlowList; f; f = f->iNext) + f->Notify(list, aState); + list.Deliver(aState); + } + +// CIp6Route::SetChanged +// ********************* +// Set iChanged for all flows on this route +TInt CIp6Route::SetChanged(const TInt aScope) const + { + if (aScope > 0) + return iInterface.SetChanged(aScope-1); + TInt count = 0; + for (CIp6Flow *f = iFlowList; f; f = f->iNext) + count += f->SetChanged(0); + return count; + } + +// +// CIp6Route::Send +// *************** +/** +// Send a packet to the attached interface, +// @param aPacket The packet to send (with info block). +// @param aSrc The source (just passed through, not significant) +// @param aMulticastLoop +// If non-zero and destination is multicast, then +// pass copy of the packet to receivers on this node. +// @return +// @li < 0, no interface of some other missing component +// @li = 0, packet sent, but interface does not want more after this +// @li = 1, packet sent, and interface is willing to accept more +// +// The packet "ownership" is always transfered, regardless of the return type, +// aPacket should be empty after this. +// +// On entry, the packet must have a info structure of RMBufSendInfo. +*/ +TInt CIp6Route::Send(RMBufChain& aPacket, CProtocolBase* aSrc, TInt aMulticastLoop) + { + TInt ret = KErrNotReady; + for (;;) // Just to provide multiple exits (break) below + { + RMBufSendInfo *const info = RMBufSendPacket::PeekInfoInChain(aPacket); + if (!info) + break; + + // "Dereference" automatically when sent to a gateway route + if (iRouter) + { + if (info->iFlags & KIpDontRoute) + break; // "routing disabled", just drop the packet! + return iRouter->Send(aPacket, aSrc, aMulticastLoop); + } + + if (Type() == ELoopback) // Use Type() to cover all loopback routes (like Anycast) + { + // Tag the packet as "loopback", so that inbound processing can easily + // recognize such packets (used in capability checks). + info->iFlags |= KIpLoopbackPacket; + // Set and clear KIpBroadcastOnLink depending on if destination is multicast. + // (this catches net broadcast and joined multicast groups). + if (iIsMulticast) + info->iFlags |= KIpBroadcastOnLink; + else + info->iFlags &= ~KIpBroadcastOnLink; + + // Do not require iNifIf on ELoopBack destinations + const TIp6Addr &dst = TInetAddr::Cast(info->iDstAddr).Ip6Address(); + // + // Assume if route is multicast, then dest is also! + // + // *NOTE* + // This branch only gets multicast routes for joined multicast + // groups (Type() == ELoopback). Beware: there are also other + // types of routes with multicast address prefix! + // + if (!iIsMulticast || dst.Scope() < KIp6AddrScopeLinkLocal) + { + // Destination is not multicast or has less than link local scope. + // This is plain loopback in the stack internally. + // No other special handling required. + iInterface.iNifUser->iNetwork->Process(aPacket, (CProtocolBase *)iInterface.iNifIf); + ret = 1; // Return 1 to indicate "more packets can be sent" + break; + } + // Destination is multicast and has wider than node local scope... + if (aMulticastLoop) + { + // The packet must be both looped back and also sent out to interface, + // a copy of the packet is needed... + RMBufPacketBase clone; + TRAP(ret, clone.CopyPackedL(aPacket)); + if (ret != KErrNone) + { + clone.Free(); // just in case... + break; + } + iInterface.iNifUser->iNetwork->Process(clone, (CProtocolBase *)iInterface.iNifIf); + ret = KErrNotReady; // (restoring the "default" return status) + } + // Continue processing the multicast packet as any other packet, sending it + // to the interface.. + } + else + { + if (iState == EIncomplete) + { + // The neighbor discovery is still in progress, need to queue + // at least ONE packet to wait for it's completion. Swap the + // content of the queue (iPacket) and current packet (aPacket) + // + // Also notify HOLD to hint flows that they shouldn't send anything + // before the ND completion... + // + NotifyFlows(EFlow_HOLD); + RMBufChain tmp(iPacket); + iPacket = aPacket; + aPacket = tmp; + ret = 1; + break; + } + else if (iInterface.NeedsND() && + (iState == EStale || + (iState == EReachable && + // iReachableTime needs to be represented in TickCount units -- msa + (User::TickCount() - iTimeStamp) > iInterface.iReachableTime))) + { + // Start the delay timer + iState = EDelay; + iRetry = 1; + // This a bit dangerous call, as iRoute->Timeout() also includes code to + // do route destruction! Should be damn sure that this call doesn't + // trigger that!!! -- msa + Timeout(); + } + if (iAddress.Family()) + { + // Copy address information into info destination address + iAddress.GetAddress(info->iDstAddr); + } + // Clear KIpBroadcastOnLink. It will be set in the Send of CIp6Interface + // if destination is IPv4 or IPv6 multicast address. + info->iFlags &= ~KIpBroadcastOnLink; + } + // If destination is still IP address and IPv4 mapped, then + // pass it to the interface as plain IPv4 address (KAfInet)! + // (even, if ND interface, this is still relevant for multicast + // destinations) + if ((TInetAddr::Cast(info->iDstAddr).IsV4Mapped())) + (TInetAddr::Cast(info->iDstAddr)).ConvertToV4(); + + return iInterface.Send(aPacket, aSrc); + } + aPacket.Free(); // NOOP, if packet ownership taken by someone. + return ret; + } + +// +// CIp6Route::StartND +// ****************** +// Start ND process. +// @param aSrc The source to be used. +void CIp6Route::StartND(const TIp6Addr &aSrc) + { + // There should be no need to test for EIncomplete. However, as some + // ND messages are also used in Point-to-Point links (RA), it is + // possible that EIncomplete routes get created for non-ND interfaces. + // Thus, NeedsND() is checked, and if not set, the route is directly + // changed into EReachable state, + ASSERT(iState == EIncomplete); + + if (!iInterface.NeedsND()) + { + iState = EReachable; + return; + } + // + // On EIncomple route the timer is always active, if + // ND is in progress. When ND terminates, fail or success, + // the route state will not be EIncomplete! + // + if (!IsTimerActive()) + { + // ...when incomplete, triggering SRC address + // is stored in the iAddress field of the route. + iAddress.SetAddress(aSrc); // ..thus, store the src + iRetry = iInterface.iND.iMaxMulticastSolicit; + Timeout(); + } + } + +// +// CIp6Route::Update +// ***************** +/** +// Update route entry. +// @param aFlags How to update +// @param The gateway (gateway routes) or link layer address (host routes). +// @param The lifetime. +// @returns +// @li 0, if state change cannot release any flows +// @li 1, if state change potentially released flows +*/ +TInt CIp6Route::Update(TInt aFlags, const TSockAddr *aAddress, const TLifetime *const aLifetime) + { +// const TUint family = aAddress ? aAddress->Family() : KAFUnspec; + const TSockAddr *const address = (aAddress && aAddress->Family() != KAFUnspec) ? aAddress : NULL; + TInt notify = 0; + switch (iState) + { + case ERedirect: + case EGateway: + if (address) + { + iAddress.SetAddress(*address); + + // Changed address of a gateway entry (includes + // case of when gateway entry is created). Must + // now "refresh" the iRouter entry! + // Should verify that Family is KAfInet6? -- msa + CIp6Route *router = iInterface.FindNeighbor(iAddress.Ip6Address()); + if (router && router->iIsRouter) + iRouter = router; + else + { + // If route was ERedirect, it should never get here (and if it + // gets, it should be deleted--however, "Update" cannot delete + // the self (this). [should be ok, ERedirect is only installed from + // ND handler, and it should make sure to install the host + // route (ISROUTER=1) before creating the redirection! --msa] + ASSERT(iState != ERedirect); + iRouter = NULL; + } + notify = 1; + } + // *FALL THROUGH* + case ELoopback: + case EOnlink: + default: + if (aLifetime) + { + // + // Reset the lifetime of this route (aLifetime should be > 0, but + // just as a safety measure, treat 0 as 1) + CancelTimer(); + ASSERT(iRetry == 0); + iRetry = 0; // should be unnecessary.. + ASSERT(*aLifetime > 0); + if (*aLifetime < KLifetimeForever) + // NEED FIXING/LOOKING INTO: There is theoretical possibility that + // timeout expires directly and deletes the route now... Should + // prevent that somehow! -- msa + SetTimer(*aLifetime ? *aLifetime : 1); + } + return notify; + // + // NEIGHBOR DISCOVERY SECTION + // -------------------------- + case EIncomplete: + // TimeStamp can be set on every update, because the reachability + // algorithm kicks in only when entry is changhed into some other + // state. Setting it here guarantees that every IsHostRoute() has + // some reasonable value for the time stamp (all such routes are + // *ALWAYS* created as first into "EIncomplete" state). TimeStamp + // can then be used in the cleanup process to detect old unused + // entries (see CIp6Interface::Timeout()). [in other states, + // updating time stamp depends on the situation and cannot be done + // unconditionally -- msa] + iTimeStamp = User::TickCount(); +#if 0 + if (address == NULL) + { + // This is dubious, might cause a fail of some ND + // conformance test. However, this is needed, because PPP + // link needs to get it's IS ROUTER bit set, even if it + // has no link layer addresses.. + // [if link layer address is missing, should ND enabled + // interface just return here, and NOT do the ISROUTER + // stuff? -- msa] + if (iInterface.iHwAddr.Family() == KAFUnspec) + { + // If interface does not use link layer addresses, + // just implicitly change into reachable state... + iState = EReachable; + } + break; + } + CancelTimer(); // if any active! + iAddress.SetAddress(*address); +#else + if (address == NULL) + { + // No address available (no target linklayer address) + if ((aFlags & KRouteAdd_UPDATEONLY) != 0 && + // UPDATEONLY is set for NA, but not for REDIRECT related + // target routes (which have ISROUTER set!). Redirect routes + // must process the "router bit", even if no link address + // is present. NA must drop the packet and NOT process + // "router bit". + iInterface.iHwAddr.Family() != KAFUnspec) + // The link layer requires addresses. If the NA didn't have the + // link layer address, drop the packet (RFC 2461, 7.2.5). + return notify; + // ..otherwise, proceed normally to update ISROUTER/ISHOST + // (but DO LEAVE THE STATE AS INCOMPLETE!!!) + break; + } + else + iAddress.SetAddress(*address); + CancelTimer(); // if any active! +#endif + iState = (aFlags & KRouteAdd_SOLICITED) ? EReachable : EStale; + if (iIsProbing) + { + iIsProbing = 0; + Interfacer().iScanHolding = 1; + } + notify = 1; + // + // Send queued packets (only one for now), if any + // + if (!iPacket.IsEmpty()) + { + // Send should not reque this packet, but just in case, + // use a tmp instead of calling Send(iPacket). + RMBufChain tmp; + tmp.Assign(iPacket); + Send(tmp); + tmp.Free(); // In case it failed. + } + break; // ..to the IS ROUTER processing + case EReachable: + case EStale: + case EDelay: + case EProbe: + // At this point, the state is one of the following and nothing else + // EReachable (no timer) + // EStale (no timer) + // EDelay (timer active) + // EProbe (timer active) + // + if (address && !iAddress.Match(*address)) + { + if (aFlags & KRouteAdd_OVERRIDE) + { + iAddress.SetAddress(*address); + iState = EStale; + CancelTimer(); // Could have been in Probe or Delay! + } + else + { + // RFC 2461 7.2.5 does not require SOLICITED to be set for setting + // STALE, but in APPENDIX C it looks like it should be required? + // + if (iState == EReachable /* && (aFlags & KRouteAdd_SOLICITED)*/) + { + iState = EStale; + // no need to cancel timer, EReachable doesn't have one! + LOG(LogRoute(KLifetimeForever)); + } + return 0; + } + } + if (aFlags & KRouteAdd_SOLICITED) + { + iState = EReachable; + CancelTimer(); // Could have been in Probe or Delay! + iTimeStamp = User::TickCount(); + LOG(if (!notify) LogRoute(KLifetimeForever)); + } + break; + } + // + // Maintain IS ROUTER status (iIsRouter + iRoute pointers) + // + // Cannot have both ISROUTER and ISHOST set! + ASSERT((aFlags & (KRouteAdd_ISHOST|KRouteAdd_ISROUTER)) != (KRouteAdd_ISHOST|KRouteAdd_ISROUTER)); + ASSERT(Type() == KRouteAdd_NEIGHBOR); // Only Host Routes should get here + if (iIsRouter && (aFlags & KRouteAdd_ISHOST) != 0) + { + // IS-ROUTER changed from TRUE to FALSE + iIsRouter = 0; + iInterface.RouterChanged(this); + } + else if (!iIsRouter && (aFlags & KRouteAdd_ISROUTER) != 0) + { + // IS-ROUTER changed from FALSE to TRUE + iIsRouter = 1; + iInterface.RouterChanged(this); + notify = 1; + } + return notify; + } + +// CIp6Route::Timeout +// ****************** +// A timeout expired for this route. + +// +// *WARNING* +// This method is used both for starting the timers and also +// as the timeout handler. When called to start the timer, iRetry +// should always be non-zero, and this method MUST NEVER destroy +// the route instance in such case...!! +// +// On real timeout, aExpired == 1 +// On start timeout, aExpired == 0 +// +// *NOTE* +// In current version SetTimeout(0) will never call directly +// expire (Timeout). Thus, it is also safe to use that +// within this method, if necessary. (one does not need to +// worry about intance being deleted away in middle of this +// method). +// +void CIp6Route::Timeout(const TInt aExpired) + { + // + // What was I doing? + // + switch (iState) + { + case EIncomplete: + if (iRetry == 0) + break; + // + // RFC-2461 7.2.2 says "...If the source address of the packet prompting the + // solicitation is the same as one of the addresses assigned to the outgoing + // interface, that address SHOULD be placed in the IP Source Address of the + // outgoing solicitation." + // + // Assume this src address is stored in iAddress at this point. Must be + // my own address (not verified here). + { +#ifdef _LOG + TLogAddressPrefix tmpsrc(iAddress.Ip6Address()); + TLogAddressPrefix tmpdst(iPrefix); + Log::Printf(_L("\tIF %u [%S] ROUTE %u Send NS from [%S] for target [%S]"), iInterface.iScope[0], &iInterface.iName, iIndex, &tmpsrc, &tmpdst); +#endif + iRetry -= 1; + // unspecified address as dst will use solicited node based on target + iInterface.SendNeighbors(KInet6ICMP_NeighborSol, NULL, iPrefix, &iAddress.Ip6Address()); + if (iIsProbing && iRetry == 0) + { + // When probing, set the last timeout specially. This will prevent + // reactivating the probing and rate limits it. + SetTimer(iInterface.iND.iRateLimitProbingTime); + } + else + Interfacer().SetTimerWithUnits(iTimeout, iInterface.iRetransTimer); + } + return; + case EDelay: + if (iRetry) + { + // A request to start the delay + iRetry = 0; + SetTimer(iInterface.iND.iDelayFirstProbeTime); + return; + } + // The requested delay has completed... + iRetry = iInterface.iND.iMaxUnicastSolicit; + iState = EProbe; + LOG(LogRoute(KLifetimeForever)); + /* FALL THFROUGH */ + case EProbe: + { + if (iRetry == 0) + { + break; + } + else if (iRetry == 1) + { + // try broadcast first otherwise it will break the route + iInterface.SendNeighbors(KInet6ICMP_NeighborSol, NULL, iPrefix); + Interfacer().SetTimerWithUnits(iTimeout, iInterface.iRetransTimer); + return; + } + else + { + iRetry -= 1; + iInterface.SendNeighbors(KInet6ICMP_NeighborSol, this, iPrefix); + Interfacer().SetTimerWithUnits(iTimeout, iInterface.iRetransTimer); + return; + } + } + + case ELoopback: + // *Note* It is assumed for now, that iPreferred == 0 + // for all ELoopback routes that represent joined multicast + // groups (=> timer expiration will delete group). Some + // other logic may be designed later -- msa +#ifdef _LOG + { + TLogAddressPrefix tmp(iPrefix, iLength); + Log::Printf(_L("\tIF %u [%S] ROUTE %u Timeout prefix [%S]"), iInterface.iScope[0], &iInterface.iName, iIndex, &tmp); + } +#endif + if (iLifetime.iPreferred > 0) + { + // Prefix is changing from preferred to deprecated + // (pick the value into temporary and zero iPreferred + // before setting the timeout, just in case timeout + // expires immediate -- ít shouldn't because of delay + // is > 0, but as it costs nothing to be safe.. -- msa + const TLifetime value = iLifetime.iPreferred; + iLifetime.iPreferred = 0; + iLifetime.iDeprecated = 1; + SetTimer(value); + LOG(LogRoute(value)); + return; + } + // + // Prefix lifetime has expired + // + iInterface.iSequence++; + break; + case EOnlink: + case EGateway: + case EReachable: + case EStale: + default: + // + // The default processing with iRetry==0 is to delete the route + // + if (iRetry == 0) + break; + iRetry = 0; + return; + + case EHolding: + // + // Holding route timeout handling is different from others: + // It should poll the held flows and "expire" too old ones! + // + const TUint max_time = Interfacer().iMaxHoldingTime; + // + // if iMaxHoldingTime == 0, no "expire" happens + // + if (max_time > 0) + { + const TUint tick_now = User::TickCount(); + + TUint tick_limit; // max_time converted into ticks (conversion code block below) + { + TReal interval; + (void)Math::Round(interval, (max_time * 1000000.0) / TickPeriod(), 0); + (void)Math::Int((TInt32 &)tick_limit, interval); + } + LOG(Log::Printf(_L("HoldingRoute: max=%d [s], tick_limit = %d, tick_now = %d"), max_time, tick_limit, tick_now)); + + TUint longest_hold = 0; + TUint flows_left = 0; // just 0 = no flows, 1= flows left holding + TFlowNotifyList list; + for (CIp6Flow *f = iFlowList; f != NULL; f = f->iNext) + { + const TUint hold_time = tick_now - f->iTimeStamp; + if (hold_time >= tick_limit) + list.Insert(*f); + else + { + // Flow still has time to wait left. Keep + // track of the longest unexpired hold (for + // setting the next timer...) + // + flows_left = 1; + if (hold_time > longest_hold) + longest_hold = hold_time; + } + } + list.Deliver(KErrInet6NoRoute); + // + // (Re)Enable polling timer, if flows still attached + // [note: longest_hold can be 0, if we just added the first flow] + if (flows_left) + { + // ... it might be better to precompute integer approximation of ticks per second + // to be used where exact timing is not so essential (instead of this floating + // arithmetic). then it would be just integer divisition with truncate: + // max_time = (tick_limit - longest_hold + ticks_per_second + 1) / ticks_per_second; + // and + // tick_limit = ticks_per_second * max_time; + // -- msa + // + TReal interval; + (void)Math::Round(interval, 1.0 * (tick_limit - longest_hold) * TickPeriod() / 1000000.0, 0); + (void)Math::Int((TInt32 &)max_time, interval); + LOG(Log::Printf(_L("HoldingRoute: next after %d ticks [= %d+1s]"), tick_limit-longest_hold, max_time)); + SetTimer(max_time + 1); // add +1 to guarantee > 0, and that surely enough time has passed + } + } + return; + } + // + // Gets here only if this route should be removed (if possible) + // (iRetry == 0) + // + // If there is any packet waiting for transmission, then report + // ICMP host/address unreachable for it (currently iPacket is + // only used for neighbor cache routes). If iPacket is used + // in some other route types, a test for the route type could + // be inserted here... + Interfacer().IcmpSend(iPacket, TIcmpTypeCode(KInet4ICMP_Unreachable, 1, KInet6ICMP_Unreachable, 3)); + + if (aExpired) + iInterface.RemoveRoute(this); // "this" is DELETED! Beware! + else + // Be tricky, and destruct with a delay... + SetTimer(1); + } + +// +// **************************** +// CIp6Interface Implementation +// **************************** +// + +CIp6Interface::CIp6Interface(CIp6Manager &aMgr, TUint aIndex, const TDesC &aName) : + iInterfacer(aMgr), iName(aName), iTimeout(CIp6InterfaceTimeoutLinkage::Timeout) + { + __DECLARE_NAME(_S("CIp6Interface")); + iScope[0] = aIndex; + LOG(Log::Printf(_L("\tIF %u [%S] New"), iScope[0], &iName)); + } + +// CIp6Interface::Index, Name and Scope +// ************************************ +// The basic information about the interface +// +TUint32 CIp6Interface::Index() const + { + return iScope[0]; + } + +const TDesC & CIp6Interface::Name() const + { + return iName; + } + +TUint32 CIp6Interface::Scope(const TScopeType aType) const + { + return ((TUint)aType > EScopeType_NET) ? 0 : iScope[aType]; + } + +// +// CIp6Interface::UpdateFlowCount +// ****************************** +// +void CIp6Interface::UpdateFlowCount(TInt aChange) + { + if (aChange == 0) + return; // No change! + iFlows += aChange; + LOG(Log::Printf(_L("\tIF %u [%S] Attached flows changed from %d to %d"), iScope[0], &iName, iFlows - aChange, iFlows)); + ASSERT(iFlows >= 0); + + if (!iNifIf || !iNifIf->Notify()) + return; // No interface attached or Nifman + // doesn't care--nothing more to do. + if (iFlows == 0) + { + // + // The flow count for this interface has gone to Zero + // Notify the NIFMAN that the interface can be closed, + // if it wants to do so + // + LOG(Log::Printf(_L("\tIF %u [%S] CloseRoute"), iScope[0], &iName)); + iNifIf->Notify()->CloseRoute(); + } + else if (iFlows == aChange) + { + // + // The flow just changed from 0 to something non-zero + // Notify NIFMAN that interface is back in use. + LOG(Log::Printf(_L("\tIF %u [%S] OpenRoute"), iScope[0], &iName, aChange)); + iNifIf->Notify()->OpenRoute(); + } + } + +// CIp6Interface::Send +// ******************* +// Send a packet to the interface +TInt CIp6Interface::Send(RMBufChain& aPacket, CProtocolBase* aSrc) + { + TInt ret = KErrNotReady; + + for (;iNifIf;) // ** NOT REAL LOOP, ONLY FOR CONVENIENT ERROR EXITS! ** + { + if (iState == EFlow_HOLD) + { + iHoldQueue.Append(aPacket); + LOG(Log::Printf(_L("\tIF %u [%S] Send holding packets"), iScope[0], &iName)); + + return 0; // Try to request "no more packets" + } + // Because Send will consume the packet, must pick up the parameters + // for the packet activity notification before it! + RMBufSendInfo *const info = RMBufSendPacket::PeekInfoInChain(aPacket); + if (info == NULL) + break; // ...should really never happen, but just in case! + const TUint bytes = (TUint)info->iLength; + const TBool reset_timer = (info->iFlags & KIpKeepInterfaceUp) != 0; + + // If destination at this point is still IPv4 or IPv6 multicast address + // set the KIpBroadcastOnLink flag. Note, that the flag is not cleared + // becuase this bit may have been correctly set earlier for broadcast + // addresses (which are not recognized as multicast here!). + if (TInetAddr::Cast(info->iDstAddr).IsMulticast()) + info->iFlags |= KIpBroadcastOnLink; + LOG(PktLog(_L("\tIF %u [%S] SEND prot=%d src=%S dst=%S len=%d"), *info, iScope[0], iName)); + ret = iNifIf->Send(aPacket, aSrc); + if (ret <= 0) + { + // The Send returns are "officially" only + // 1 = Packet Accepted ok + // 0 = Packet Accepted, don't send more before StartSending + // However, some may return < 0 to signal an error (and not + // follow it up with start sending). So, the following logic + // is implemented: + // Returned 1, no change in interface state or flow [does not enter this branch of code] + // Returned 0, interface and flows are set to HOLD + // Returned < 0, no change in interface state, error is + // reported to the flows! + LOG(Log::Printf(_L("\tIF %u [%S] NIF Send returned HOLD (%d)"), iScope[0], &iName, ret)); + TInt state = ret; + if (state == 0) + iState = state = EFlow_HOLD; // Convert "0" to HOLD state + NotifyFlows(state); + } + if (iNifIf->Notify()) + (void)iNifIf->Notify()->PacketActivity(EOutgoing, bytes, reset_timer); + + // If the packet ownership is not taken at this point, someone is not + // working as specified (Process() or Send()). + ASSERT(aPacket.IsEmpty()); + break; // ** MUST TERMINATE THE "FAKE FOR"-LOOP ALWAYS! + } + aPacket.Free(); // If nobody took it, release it! + return ret; + } + +// +// CIp6Interface::UpdateMulticast +// ****************************** +/** +// Maintain multicast membership status at low level (add/remove the +// route and update the interface; must not do MLD!) +// +// @param aMulticast speficies the multicast group to join or leave +// @param aLifetime, = 0 => leave group, != 0, join group. (the life time +// of multicast group is controlled by join/leaves, and the actual non-zero +// value is not used for anything else except as a flag). +// +// @returns +// @li KErrNone, if join or leave succeeds, +// @li KErrNotFound, if leave for non-existent group +// @li KErrNoMemory, if group cannot be joined due to memory allocation failures +// @li any other error, if NIF rejects the join . +*/ +TInt CIp6Interface::UpdateMulticast(const TIp6Addr &aMulticast, const TLifetime aLifetime) + { + // + // Construct Join/leave option buffer + // + TPckgBuf opt; + opt().iAddr = aMulticast; + opt().iInterface = iScope[0]; + // + // NIF needs to be notified only if it exists, and if the scope + // is larger than node-local. NIF can use this notify + // to maintain multicast filters for the interface, + // if it supports such feature. + const TBool notify_nif = (iNifIf != NULL) && (aMulticast.Scope() > KIp6AddrScopeNodeLocal); + // + // Get the multicast route entry representing the group + // + CIp6Route *route = GetRoute(aMulticast, 128, KRouteAdd_MYPREFIX|KRouteAdd_UPDATEONLY); + if (route == NULL) + { + // + // The multicast group does not exist yet. + // + if (aLifetime == 0) + return KErrNotFound; // not joined to this group, cannot leave. + + if (notify_nif) + { + const TInt err = iNifIf->Control(KSolInetIp, KSoIp6JoinGroup, opt); + if (err < 0 && err != KErrNotSupported) + return err; // Interface explicitly rejects the join. + } + route = GetRoute(aMulticast, 128, KRouteAdd_MYPREFIX); + if (route == NULL) + return KErrNoMemory; + // note: above GetRoute just created a special multicast "myprefix" + // route entry. This is exactly same as joining to the multicast + // group. Could consider generating the NotifyMulticastEvent from + // the "address route creation event", and not sending the route + // event in such case at all (implementation of joined multicast + // groups as "multicast my-address" entries is internal implementation + // issue). -- msa + NotifyMulticastEvent(EventTypeAdd, aMulticast, aLifetime); + return KErrNone; + } + + // Join or Leave to an existing group. The iLifetime.iCount + // counts the *ADDITIONAL* users after the first one. With one + // user, the iCount == 0! + + ASSERT(route->iIsMulticast); + + if (aLifetime) + { + // + // Additional join to an existing multicast group + // + route->iLifetime.iCount++; + } + else if (route->iLifetime.iCount == 0) + { + // + // Last user left the group + // + NotifyMulticastEvent(EventTypeDelete, aMulticast, aLifetime); + // note: generates remove of "multicast myprefix" entry, above + // multicast notify could be generated from that.. -- msa + RemoveRoute(route); + if (notify_nif) + (void)iNifIf->Control(KSolInetIp, KSoIp6LeaveGroup, opt); + } + else + { + // + // Non-last user left the group + // + route->iLifetime.iCount--; + } + return KErrNone; + } + +// CIp6Interface::DoBind +// ********************* +/** +// Finalizes the connection between the network and interface, assuming +// the CNifIfBase instance is connected to the interface +// +// @note +// Actually combined "close/rebind/open" method, which closes any +// existing bindings, and sets the new binding (if provided). +// There should probably be separate Open/Close methods. +*/ +TInt CIp6Interface::DoBind(CIp6NifUser *aNifUser, CNifIfBase *aIf) + { + ASSERT(aNifUser != NULL); + + iNifUser = aNifUser; // Do this always! + + if (iNifIf == aIf) + return KErrNone; // Do nothing, already bound to this (or both NULL)! + + Reset(); // ...back to initial state! + if (aIf) + { + // + // A new NIF interface to bind + // + aIf->Open(); + iNifIf = aIf; + + if (aNifUser->iNetwork) + { + TRAPD(err, aIf->BindL(aNifUser->iNetwork->Protocol())); + if (err != KErrNone) + { + // + // Bind failure + // + iNifIf = NULL; + aIf->Close(); + } + else + { + // ...notify network layer, in case any hook is interested... + // [The above BindL may have caused StartSending and other + // large actions to happen, so just to be safe, need to + // check that iNetwork is still attached... -- msa] + if (iNifUser->iNetwork) + iNifUser->iNetwork->InterfaceAttached(iName, iNifIf); +/* if (iFlows > 0 && aIf->Notify()) + aIf->Notify()->OpenRoute();*/ + } + return err; + } + else + { + // Interface instance exists, but there is no network to + // connect. What to do? Should be left into pending state + // until the network registers and then call the BindL? + // -- msa + User::Panic(_L("DEBUG"), 0); + } + } + return KErrNotReady; + } + + +// +// MakeFullAddress +// *************** +static void MakeFullAddress(TIp6Addr &aPrefix, TUint aLength, const TUint8 *aId, TInt aIdLen) + /** + * Combine hardware id and prefix into single address. + * + * If ID is longer than available room in address, the extra bits + * from the start of the ID are ignored. + * + * If ID is shorter than available room in address, the unspecified + * bits in the ID part will be ZERO. + * + * @retval aPrefix The prefix part of the address, and final address on return. + * @param aLength The prefix length in BITS. + * @param aId The Id value + * @param aIdLen The id length in full bytes. + */ + { + TInetAddr msk; + msk.PrefixMask(aLength); + + // Initialize properly aligned ID + // (copy id to the end of TIp6Addr and zero remaining) + TIp6Addr id; + TInt i = sizeof(id.u.iAddr8); + do + { + id.u.iAddr8[--i] = --aIdLen >= 0 ? aId[aIdLen] : (TUint8) 0; + }while(i > 0); + // Added explicit cast (TUint8) above to suppress WINS compiler warning + + // Merge id with prefix + const TIp6Addr &m = msk.Ip6Address(); + +/* for (TInt j = 4; --j >= 0;) + aPrefix.u.iAddr32[j] = (aPrefix.u.iAddr32[j] & m.u.iAddr32[j]) | (id.u.iAddr32[j] & (~m.u.iAddr32[j])); +*/ + // We don't loop. Just do the calculation. + aPrefix.u.iAddr32[3] = (aPrefix.u.iAddr32[3] & m.u.iAddr32[3]) | (id.u.iAddr32[3] & (~m.u.iAddr32[3])); + aPrefix.u.iAddr32[2] = (aPrefix.u.iAddr32[2] & m.u.iAddr32[2]) | (id.u.iAddr32[2] & (~m.u.iAddr32[2])); + aPrefix.u.iAddr32[1] = (aPrefix.u.iAddr32[1] & m.u.iAddr32[1]) | (id.u.iAddr32[1] & (~m.u.iAddr32[1])); + aPrefix.u.iAddr32[0] = (aPrefix.u.iAddr32[0] & m.u.iAddr32[0]) | (id.u.iAddr32[0] & (~m.u.iAddr32[0])); + } + + +// +// CIp6Interface::Elapsed +// ********************** +TLifetime CIp6Interface::Elapsed(const TTime &aStamp) const + { + TTimeIntervalSeconds elapsed; + aStamp.SecondsFrom(iTimeStamp, elapsed); + TInt elapsedInt = elapsed.Int(); + // Return 0, if time is earlier than time stamp (clock turned back?) + return (TLifetime) (elapsedInt < 0 ? 0 : elapsedInt); + } + +// +// CIp6Interface::SetPrefix() +// ************************** +// +// *NOTE* to delete a prefix, aLifeTime == 0 (and set aForce non-ZERO, if 2h safeguard +// is to be disabled) +// +void CIp6Interface::SetPrefix(const TIp6Addr &aPrefix, const TUint aLength, const TInt aForce, const TLifetime aLifetime, const TLifetime aPreferred) + { + ASSERT(aLength <= 128); +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::SetPrefix() lifetime = (%d)"),aLifetime)); + #endif +#endif // SYMBIAN_TCPIPDHCP_UPDATE + if (TIp46Addr::Cast(aPrefix).IsMulticast() || + // ...basicly, all prefixes on the interface should have the same + // length (128 - idlength). However, make an exception for special + // interfaces with with idlength = 0 (like 6to4). [Note: idlength + // is not directly stored, but computed as (128-iAddress.iPrefix)] + (iAddress.iPrefix != 128 && iAddress.iPrefix != aLength && aLength != 128)) + return; // silently ignore all attemps to put in funny prefixes! + + CIp6Route *prefix = GetRoute(aPrefix, aLength, + // Set UPDATEONLY, if lifetime is ZERO (don't create!) + KRouteAdd_MYPREFIX | (aLifetime > 0 ? 0 : KRouteAdd_UPDATEONLY) + ); + if (prefix == NULL) + return; // OOPS, out of memory or something... + + // Lifetime is maintained relative to the iTimeStamp of the interface + TTime stamp; + stamp.UniversalTime(); + const TLifetime current_time = Elapsed(stamp); + // Compute old remaining lifetime (StoredLifetime in RFC 2462/5.5.3) + + TLifetime storedLifetime = (prefix->iLifetime.iStored > current_time) ? + prefix->iLifetime.iStored - current_time : 0 /* 0 = expired*/; + + // The logic we are counting on here: if storedLifetime above has something set, + // this must have been an earlier existing prefix, hence EventTypeModify. + // Otherwise it is a new prefix. + TUint eventtype = (storedLifetime ? EventTypeModify : EventTypeAdd); + + const TUint two_hours = 7200; //2 * 60 * 60; in seconds + if (aForce || aLifetime > two_hours || aLifetime > storedLifetime) + { + storedLifetime = aLifetime; + } + else if (storedLifetime >= two_hours || aLifetime >= storedLifetime) + { + storedLifetime = two_hours; + } + // + // If after above, the storedLifetime > 0, then this prefix should + // still remain. + if (storedLifetime > 0) + { + + // Set new stored time, but watch out for overflow + + if (storedLifetime > KLifetimeForever - current_time) + prefix->iLifetime.iStored = KLifetimeForever; + else + prefix->iLifetime.iStored = current_time + storedLifetime; + // + // The iPreferred contains the duration of the "deprecated" + // time before true expiration occurs (in seconds) + // + // 0 <= iPreferred <= storedLifetime + // + if (aPreferred < storedLifetime) + prefix->iLifetime.iPreferred = storedLifetime - aPreferred; + else + prefix->iLifetime.iPreferred = 0; + prefix->iLifetime.iDeprecated = 0; + + // Set active timer only if the lifetime is less than "forever". + const TLifetime life = storedLifetime - prefix->iLifetime.iPreferred; + if (life < KLifetimeForever) + prefix->SetTimer(storedLifetime - prefix->iLifetime.iPreferred); + else + { + prefix->CancelTimer(); + } +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + if(iGlobalflag) + { + //If the global flag is set(this will be set when the 'A' flag is set in the prefix of RA) + //then do DAD for the Global address + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::SetPrefix() glbal flag is set"))); + #endif + PerformDADForGlobalAddress(aPrefix,aLength); + iGlobalflag=EFalse; + } +#endif // SYMBIAN_TCPIPDHCP_UPDATE + // Send notification about new prefix to the event service + NotifyAddressEvent(eventtype, aPrefix, aLength, prefix, iAddress); + } + else + { + // Send notfification about deleted prefix to the event service + NotifyAddressEvent(EventTypeDelete, prefix->iPrefix, prefix->iLength, + prefix, iAddress); + + iSequence++; + RemoveRoute(prefix); + } + } + + +void CIp6Interface::NotifyAddressEvent( TUint aEventType, + const TIp6Addr &aPrefix, + const TUint aLength, + const CIp6Route *aPrefixEntry, + const TIp6AddressInfo &aAddress ) const + /** + * Send notification about changed address to event manager. + * @param aEventType The event type code (see in_bind.h). + * @param aPrefix Prefix part of the address. + * @param aLength Prefix length. + * @param aPrefixEntry Pointer to the KRouteAdd_MYPREFIX entry in routing table + * NULL indicates this is was an addition of ID part of the + * address. + * @param aAddress Information about the ID part of the address. + */ + { + CIp6Manager *const mgr = &Interfacer(); + + // If there is no event manager, or if there are no registered listeners, we can exit + // the function right away + if (!mgr->EventManager()) + { + return; + } + + if (mgr->EventManager()->IsEmpty(EClassAddress)) + { + return; + } + + TInetAddressInfo info; + info.iAddress = aPrefix; + if (aPrefixEntry) + { + MakeFullAddress(info.iAddress, aLength, + iAddress.iId.u.iAddr8, sizeof(iAddress.iId.u.iAddr8)); + } + + TScopeType st = (TScopeType) (aPrefix.Scope() - 1); + info.iScopeId = Scope(st); + info.iPrefixLen = (TUint8) aLength; + info.iInterface = Index(); + + TTime stamp; + stamp.UniversalTime(); + const TLifetime current_time = ElapsedUnits(aAddress.iCreated, stamp); + + TLifetime plt, vlt; + + if (aAddress.iPLT != KLifetimeForever) + { + plt = (aAddress.iPLT > current_time) ? + aAddress.iPLT - current_time : 0 /* 0 = expired*/; + } + else + { + plt = KLifetimeForever; + } + + if (aAddress.iVLT != KLifetimeForever) + { + vlt = (aAddress.iVLT > current_time) ? + aAddress.iVLT - current_time : 0 /* 0 = expired*/; + } + else + { + vlt = KLifetimeForever; + } + + info.iPrefLifetime = plt / TIMER_UNIT; + info.iValidLifetime = vlt / TIMER_UNIT; + info.iGenerations = aAddress.iGenerated; + info.iNS = aAddress.iNS; + info.iState = (TUint8) aAddress.AddressState(); + info.iType = (TUint8) aAddress.AddressType(); + info.iFlags = 0; + + if (aPrefixEntry == NULL) + { + info.iFlags |= TInetAddressInfo::EF_Id; + if (plt == 0) + { + info.iFlags |= TInetAddressInfo::EF_Deprecated; + } + } + else + { + info.iFlags |= TInetAddressInfo::EF_Prefix; + if (aPrefixEntry->iLifetime.iDeprecated) + { + info.iFlags |= TInetAddressInfo::EF_Deprecated; + } + } + + mgr->EventManager()->Notify(EClassAddress, aEventType, &info); + } + +// +// CIp6Interface::SetReachableTime +// ******************************* +/** +// Compute the in use value for the reachable time. The +// value is stored in TickCount units! +// +// The iND is assumed to containt the base value in milliseconds +*/ +void CIp6Interface::SetReachableTime() + { + const TUint tick = TickPeriod(); + + TReal factor = iND.iMinRandomFactor + Math::FRand(Interfacer().iSeed) * (iND.iMaxRandomFactor - iND.iMinRandomFactor); + (void)Math::Round(factor, (iND.iReachableTime * factor * 1000.0) / tick, 0); + (void)Math::Int((TInt32 &)iReachableTime, factor); + LOG(Log::Printf(_L("\tIF %u [%S] ReachableTime base=%d [ms], new time = %d [tics = %ds]"), iScope[0], &iName, iND.iReachableTime, iReachableTime, (TInt)((iReachableTime * tick) / 1000000))); + } + +// CIp6Interface::SetRetransTimer +// ****************************** +/** +// Compute the in use value for the retrans timer. The value +// is stored in internal timer units. +// +// The iND is assumed to contain the base value in milliseconds +*/ +void CIp6Interface::SetRetransTimer() + { + iRetransTimer = CIp6Manager::TimerUnits(iND.iRetransTimer, 1000); + if (iRetransTimer == 0) + iRetransTimer = 1; // Never allow ZERO! + LOG(Log::Printf(_L("\tIF %u [%S] RetransTimer base=%d, value = %u/%u s"), + iScope[0], &iName, iND.iRetransTimer, iRetransTimer, TIMER_UNIT)); + } + +// +// CIp6Interface::SelectSource +// *************************** +/** +// Select and set the source address matching the +// specified destination address. +// +// @retval aSrc The selected source address. +// @param aDst The destination. +// +// @return +// @li route pointer (MYPREFIX), if source address is fully specified +// @li NULL, if source address is not know or incomplete +*/ +CIp6Route *CIp6Interface::SelectSource(TIp6Addr &aSrc, const TIp6Addr &aDst) const + { + CIp6Route *best_match = NULL; + TInt best_score = KMinTInt; + const TUint scope = aDst.Scope(); // Prefetch destination scope + const TBool is_ip4 = aDst.IsV4Mapped(); // Prefetch destination type + + // If destination is my own address, the source address will + // be the destination address. Prepare for detecting this + // by prefetching the matching ID part, if any exists. + // + const TIp6AddressInfo *myid = IsMyId(aDst); + if (myid && !myid->IsAssigned()) + myid = NULL; + // + // Choose the prefix part + // + for (CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + { + if (!rt->IsMyPrefix()) + continue; // Not a "my prefix" entry, get next. + // + // The route entry represents a prefix entry + // + const TInt match = rt->iPrefix.Match(aDst) - rt->iLength; + if (myid && match >= 0) + { + // A quick hack to prevent choosing proxy or anycast + // address as a source address (they are currently + // entered as 128 bit adressess and "aDst ~ myid ~ + // prefix"... -- msa + if (!myid->IsNormal()) + continue; + // The ID part of the destination has already matched, + // and now a full MYPREFIX matched => the destination + // is my own address on this interface, + // return aDst as a source! + aSrc = aDst; + return rt; + } + // For other than own addresses, consider the prefix only + // if the primary id length and this prefix have a compatible + // length... [somewhat kludgy way to prevent 2002:7f::/24 from + // being chosen over 2002:ip4::ip4/128 .. --msa] + // (make an exception for full 128 bit addresses) + if (rt->iLength < 128 && rt->iLength != iAddress.iPrefix) + continue; + // IPv4 addresses are currently stored as full 128 bit + // addresses, the IPv4 mapped test is only needed for them. + if (is_ip4 != (rt->iLength == 128 && rt->iPrefix.IsV4Mapped())) + continue; // Mismatched IPv4 / IPv6! + + // Prefer matches that cover full prefix, thus use the difference + // between matched bits and prefix length as a criteria, with + // additional criteria that if the match is longer than prefix, + // the comparison value will be the prefix length. + // -- msa + TInt weight = match >= 0 ? rt->iLength : match; + if (rt->iLifetime.iDeprecated) + // A deprecated prefix. Consider it, but decrease it's comparison + // value by 128, so that it won't be selected if there is even one + // non-deprecated choice available. + weight -= 128; + + const TUint src_scope = rt->iPrefix.Scope(); + if (src_scope < scope) + weight -= 256; // use src with smaller scope only as last ditch. + + if (weight > best_score || (weight == best_score && src_scope == scope)) + { + best_score = weight; + best_match = rt; + } + } + if (!best_match) + return NULL; // Cannot find source address (no prefix!) + + // Kludge: if the prefix is 128 bits, use it as is for source address! + // However, there *SHOULD* be a corresponding address entry with + // id->iPrefix==0 + if (best_match->iLength == 128) + { + for (const TIp6AddressInfo *id = &iAddress; ;id = &id->iNext->iInfo) + { + if (id->iPrefix == 0 && id->iId.IsEqual(best_match->iPrefix)) + { + if (!id->IsAssigned()) + return NULL; + aSrc = best_match->iPrefix; + return best_match; + } + if (id->iNext == NULL) + break; + } + return NULL; + } + // + // Choose the id part + // + myid = NULL; + for (const TIp6AddressInfo *id = &iAddress; ;id = &id->iNext->iInfo) + { + if (id->IsAssigned() && id->IsNormal()) + { + if (best_match->iLength <= id->iPrefix) + { + // + // iGenerated is non-zero if id is randomly generated. + // This may be used in privacy address. + // Test + // iGenerated == 0, prefer random id + // iGenerated != 0, prefer non-random id. + // + if (myid == NULL) + myid = id; + else if (myid->iGenerated != 0) + myid = id; + } + } + if (id->iNext == NULL) + break; + } + if (myid) + { + aSrc = best_match->iPrefix; + MakeFullAddress(aSrc, best_match->iLength, myid->iId.u.iAddr8, sizeof(myid->iId.u.iAddr8)); + return best_match; + } + return NULL; + } + +// CIp6Interface::UpdateIdRoutes +// ***************************** +/** +// Maintains internal, address related route entries. +// +// Somewhat "ad hoc" code: maintain "solicited" node +// multicast addresses on the Route list for all id's. +// (the ad hoc part is in how this feature is activated +// by condition: 0 < aPrefix < 128, and only for IPv6. +// +// Also, maintain host loopback routes for configured +// alias addresses. +*/ +void CIp6Interface::UpdateIdRoutes(const TIp6AddressInfo &aId, const TLifetime aLifetime) + { + if (!aId.IsSet()) + return; // Nothing to do with unspecified address. + if (aId.iPrefix == 0 && !aId.IsProxy()) + { + const TUint flags = aId.IsAnycast() ? CIp6Route::EAnycast : CIp6Route::ELoopback; + (void)GetRoute(aId.iId, 128, flags, NULL, &aLifetime); + } + + // IPv6, each own id needs to recognize the corresponding solicited + // node multicast destination... + if (!aId.iId.IsV4Mapped() && aId.iPrefix < 128) + { + // ..or, should require that the Id part is at least + // 24 bits long, before solicited node is generated + // (iPrefix <= 104) -- msa + // Delete or Create entry (depending on aLifetime) + UpdateMulticast(TSolicitedNodeAddr(aId.iId), aLifetime); + } + + // If this ID is being removed make sure all routes using this ID as a source + // address or prefix are also removed or else SelectSource will be confused if + // it tries to reuse a route which no longer has a corresponding source address. + if( aLifetime == 0 ) + { + CIp6Manager &mgr = Interfacer(); + CIp6Route *rt; + + for (CIp6Route **h = &iRouteList; ; ) + { + rt = *h; + + if( !rt ) + { + // Stop. + break; + } + else + { + if( aId.MatchExactly( rt->iAddress.Ip6Address() ) || aId.MatchExactly( rt->iPrefix ) ) + { + // Remove the the route from the current position. + *h = rt->iNext; + + LOG(rt->LogRoute(0)); + + // + // Delete matching route, if lifetime is ZERO + // + if (rt->iIsRouter) + { + rt->iIsRouter = 0; + RouterChanged(rt); + } + // + // If any flows are attached to the route that is being removed, + // move them all into the holding route with PENDING status. + // + // Note: holding is *ALWAYS* non-NULL. The only time holding + // can be NULL, is when it is being created by InitL(), and in + // that case GetRoute() *NEVER* gets into this branch! -- msa + // + mgr.MoveToHolding(*rt); + + // Send notification about removed route to event manager + NotifyRouteEvent(EventTypeDelete, rt); + + delete rt; + } + else + { + h = &rt->iNext; + } + } + } + } + } + + +void CIp6Interface::NotifyMulticastEvent(TUint aEventType, const TIp6Addr &aMulticast, const TLifetime aLifetime) const +{ + CIp6Manager *const mgr = &Interfacer(); + + // If there is no event manager, or if there are no registered listeners, we can exit + // the function right away + if (!mgr->EventManager()) + return; + + if (mgr->EventManager()->IsEmpty(EClassMulticast)) + return; + + TInetMulticastInfo info; + info.iMulticastGroup = aMulticast; + info.iInterface = iScope[0]; + info.iLifetime = aLifetime; + + mgr->EventManager()->Notify(EClassMulticast, aEventType, &info); +} + + +// +// CIp6Interface::SetId +// ******************** +/** +// @retval aId The address/id to be modified. +// @param aAddr The new address. +// @param aPrefix The length of the prefix part. +// @param aAddressType Type of the address. +// @return +// @li 0, if ID was not changed +// @li 1, if ID changed +// +// Although, the main point is the id-part, it is assumed that the +// value stored as ID is also *ALWAYS* a valid full address for this +// interface (some code may depend on it!) +*/ +TInt CIp6Interface::SetId(TIp6AddressInfo &aId, const TIp6Addr &aAddr, const TInt aPrefix, const TInt aAddressType) + { + // Should this also check whether address type is same? + // Changing just type does not work with this code! + // -- msa 24.10.2003 + if (aId.IsSet() && aPrefix == aId.iPrefix && aAddr.IsEqual(aId.iId)) + return 0; // Id is same as before, no change! + if (aPrefix < 0 || aPrefix > 128) + return 0; // Invalid length, do nothing! + if (TIp46Addr::Cast(aAddr).IsMulticast()) + return 0; // A multicast address cannot be my own. + + UpdateIdRoutes(aId, 0); // Remove old route (if needed) + aId.iId = aAddr; + aId.iPrefix = (TUint8)aPrefix; + aId.SetInitial(NeedsND()); + aId.SetType(aAddressType); + aId.iNS = 0; + aId.iCreated.UniversalTime(); + aId.iVLT = KLifetimeForever; + aId.iPLT = KLifetimeForever; + UpdateIdRoutes(aId, KLifetimeForever); // Add new route (if needed) + + // Send notification about the new address to the event service + NotifyAddressEvent(EventTypeAdd, aAddr, aPrefix, NULL, aId); + + // ..should activate Timeout for DAD detection!? -- mas + + return 1; // Id has been changed + } + +TInt CIp6Interface::AddId(const TSockAddr& aId) + { + // Should get the true length of the id part. The code below works only + // for id with length of full bytes... -- msa + const TInt prefix = 128 - (*(TSockAddr *)&aId).GetUserLen() * 8; + + // Setting id of length ZERO (prefix == 128) does nothing + // and returns "nothing changed"... + if (prefix >= 0 /*&& prefix < 128*/) + { + TIp6Addr local(KInet6AddrLinkLocal); + // + // Add "ONLINK" route for all link locals + // + (void)GetRoute(local, 10, KRouteAdd_ONLINK); + + MakeFullAddress(local, prefix, aId.Ptr(), aId.Length()); + // Need to set ID before prefix (SetPrefix does some checks that + // require a known id length on the interface...) + TInt ret = AddId(local, prefix); + // AddId is only used for IPv6 interfaces, add the link local "prefix" + // here. [totally add hoc rule: set the link local prefix only if + // idlen > 0!] + SetPrefix(local, prefix, 1); + return ret; + } + return 0; + } +// +// CIp6Interface::AddId +// ******************** +/** +// This will define the primary ID, if not yet specified, or adds +// a new id. +*/ +TInt CIp6Interface::AddId(const TIp6Addr &aId, const TInt aPrefix, const TInt aAddressType, const TBool aForcePrimary) + { + ASSERT(!aId.IsUnspecified()); + // + // First would need to check if any of the id's match for this address + // + TIp6AddressInfo *prevID = NULL, *id; + for (id = &iAddress; ;prevID = id, id = &id->iNext->iInfo) + { + // Compare id's as full addresses. + // Note: here comparing just address is correct. Address type + // can only be one of the following: normal, proxy, anycast, etc. + if (id->IsSet() && id->iId.IsEqual(aId)) + { + if( aForcePrimary && !id->IsPrimary() ) + { + // We need to move this address into the primary slot. + CIp6Address *oldPrimary = new CIp6Address; + if (oldPrimary == NULL) + return 0; + oldPrimary->iInfo = iAddress; + oldPrimary->iInfo.SetPrimary( EFalse ); + + iAddress = *id; + iAddress.SetPrimary( ETrue ); + iAddress.iIpv4LinkLocal = EFalse; // reset this flag in case a link local formerly occupied this slot + iAddress.iNext = oldPrimary; + + if( prevID ) + { + prevID->iNext = id->iNext; + } + delete id; + + id = &iAddress; + } + + break; + } + if (id->iNext == NULL) + { + // None matched + // + if (!iAddress.IsSet()) + { + // Primary ID slot is still empty, use it! + id = &iAddress; + iAddress.SetPrimary( ETrue ); + + break; + } + + // + // Primary id slot is used, need to create a new entry + // + if( aForcePrimary ) + { + // We need to move this address into the primary slot. + CIp6Address *oldPrimary = new CIp6Address; + if (oldPrimary == NULL) + return 0; + oldPrimary->iInfo = iAddress; + oldPrimary->iInfo.SetPrimary( EFalse ); + + iAddress.SetPrimary( ETrue ); + iAddress.iIpv4LinkLocal = EFalse; // reset this flag in case a link local formerly occupied this slot + iAddress.iNext = oldPrimary; + + id = &iAddress; + } + else + { + CIp6Address *p = new CIp6Address; + if (p == NULL) + return 0; + p->iInfo.iNext = iAddress.iNext; + + iAddress.iNext = p; + + id = &p->iInfo; + } + + break; + } + } + return SetId(*id, aId, aPrefix, aAddressType); + } + +// CIp6Interface::GetId +// ******************** +// Locate Id block by address +TIp6AddressInfo* CIp6Interface::GetId(const TIp6Addr &aAddr) const + { + for (const TIp6AddressInfo *id = &iAddress; ;id = &id->iNext->iInfo) + { + // Compare id's as full addresses + if (id->IsSet() && aAddr.IsEqual(id->iId)) + // Throw away 'const' -- hopefully this does not cause any + // compiler problems... -- msa + return (TIp6AddressInfo *)id; + if (id->iNext == NULL) + break; // None found! + } + return NULL; + } + +// +// CIp6Interface::RemId +// ******************** +// Remove specified Id. +TInt CIp6Interface::RemId(const TIp6AddressInfo *const aId) + { + if (aId == NULL) // For convenience, allow call with NULL ptr. + return KErrNotFound; + + UpdateIdRoutes(*aId, 0); // Remove old route (if needed) + ++iSequence; // Always increment (does not hurt, even if no deletion actually happens) + + // Note: event is generated, even if no matching address is found + NotifyAddressEvent(EventTypeDelete, aId->iId, aId->iPrefix, NULL, *aId); + + if (aId == &iAddress) + { + // + // Removing the primary Id is a special case + // + if (iAddress.IsTentative()) + iAddress.SetDuplicate(); + else + iAddress.SetNoAddress(); + return KErrNone; + } + + CIp6Address **h, *p; + for (h = &iAddress.iNext; (p = *h) != NULL; h = &p->iInfo.iNext) + if (aId == &p->iInfo) + { + *h = p->iInfo.iNext; + delete p; + return KErrNone; + } + return KErrNotFound; + } + + +// +// GetIp4Config +// ************ +/** +// A simple code that initializes the TSoInetIfConfig structure +// properly and performs the query to the interface. Not a general +// method, but just way to minimize code size (used from different +// places) +*/ +static TInt GetIp4Config(CNifIfBase *aIf, TPckgBuf &cfg) + { + TSoInetIfConfig *const c = &cfg(); + + c->iFamily = KAfInet; + + // Ip4 interfaces are picky about the family constant, + // and TInetAddr initialize into family KAfInet6. + // The following will turn them into KAfInet + // -- msa + c->iConfig.iAddress.SetAddress(0); + c->iConfig.iNetMask.SetAddress(0); + c->iConfig.iDefGate.SetAddress(0); + c->iConfig.iBrdAddr.SetAddress(~0U); + c->iConfig.iNameSer1.SetAddress(0); + c->iConfig.iNameSer2.SetAddress(0); + return aIf ? aIf->Control(KSOLInterface, KSoIfConfig, cfg) : KErrNotFound; + } + + +// CIp6Interface::IsMyId +// ********************* +TIp6AddressInfo *CIp6Interface::IsMyId(const TIp6Addr &aAddr) const + { + // + // Find longest matching and usable id (the length of the id is "128 - iPrefix"). + // + const TIp6AddressInfo *best_id = NULL; + for (const TIp6AddressInfo *id = &iAddress; ;id = &id->iNext->iInfo) + { + if ((best_id == NULL || best_id->iPrefix < id->iPrefix) && + id->IsSet() && + id->Match(aAddr)) + { + best_id = id; + } + if (id->iNext == NULL) + break; + } + // Throw away 'const' + return (TIp6AddressInfo *)best_id; + } + +// CIp6Interface::IsMyPrefix +// ************************* +CIp6Route *CIp6Interface::IsMyPrefix(const TIp6Addr &aAddr, const TIp6AddressInfo &aId) const + { + for (const CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + { + if (!rt->IsMyPrefix()) + continue; + // The prefix is examined, only if + // aId.iPrefix == 128 (0x80, special, id length is 0, any prefix will do), or + // aId.iPrefix == 0 (0x00, id is full address, just pick any matching prefix) + // aId.iPrefix == rt->iLength (otherwise, id and prefix must have matching lengths). + if ((aId.iPrefix & 0x7f) != 0 && aId.iPrefix != rt->iLength) + continue; // id must be zero length or match the prefix length. + if (rt->iPrefix.Match(aAddr) >= rt->iLength) + return (CIp6Route *)rt; + } + return NULL; + } + +// +// CIp6Interface::IsMyAddress +// ************************** +/** +// IsMyAddress returns non-NULL, if aAddr matches any of the +// current addresses for this interface +*/ +TIp6AddressInfo *CIp6Interface::IsMyAddress(const TIp6Addr &aAddr, const TInt aAll) const + { + // + // First would need to check if any of the id's match for this address + // (and only properly assigned id can be "my address") + // + TIp6AddressInfo *id = IsMyId(aAddr); + if (id == NULL || !id->IsAssigned()) + return NULL; + // + // proxy/anycast address are not normal "my addresses" + // (they cannot be used as a source address) + // + if (aAll == 0 && !id->IsNormal()) + return NULL; + // + // If id part is 128 bits, then no further tests are required, + // and otherwise need to check that address matches some prefix. + // + if (id->iPrefix == 0 || IsMyPrefix(aAddr, *id)) + return id; // This is my address! + // + // None of the prefixes match + // + return NULL; + } +// +// CIp6Interface::IsForMeAddress +// ***************************** +TBool CIp6Interface::IsForMeAddress(const TIp6Addr &aAddr) const + { + // + // IsForMeAddress is TRUE. if the address is IsMyAddress or matches + // any of the "multicast" addresses configured for the interface + // (for IPv4 "multicast" includes the broadcast addresses). This + // is logically two different passes over the iRouteList, but as + // this method is expected to be used a lot, the both loops have + // been merged here... -- msa + + + // First check if any of the id's match for this address. If none + // matches, then only the "multicast" addresses need to be + // tested. + const TIp6AddressInfo *id = IsMyId(aAddr); + if (id == NULL || !id->IsAssigned() || id->IsProxy()) + // Not yet assigned or is a proxy address (not really for me) + id = NULL; + else if (id->iPrefix == 0) + // Full configured address matched, no need for further tests + return TRUE; + + // Examine ELoopback entries in the route list for match + for (const CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + { + if (rt->iState != CIp6Route::ELoopback) + continue; // Not my prefix or multicast... + if (rt->iIsMulticast) + { + // "Multicast" entries are always specified as 128 bit + // prefixes => use IsEqual, which is faster than Match! + if (aAddr.IsEqual(rt->iPrefix)) + return TRUE; // Matched fully a multicast, it's for me! + } + else if (id) + { + // Prefixes need to be compared only if Id matched! + if (rt->iPrefix.Match(aAddr) >= rt->iLength) + return TRUE; // Matched a prefix and id, it's my address! + } + } + return FALSE; // No match, not for me! + } + +// +// CIp6Interface::SetMtu +// ********************* +/** +// SetMtu *defines* the current send MTU for the link, and +// updates the Path MTU in case it is obviously affected. +*/ +void CIp6Interface::SetMtu(TInt aMtu, TInt aMin) + { + iSMtu = aMtu; + // + // This may not be the correct solution, but assume + // this method is not called often (and usually only + // on startup), thus assume that this can be used to + // initiate the Path MTU discovery and set the path + // MTU same local link mtu [currently the only way + // to get larger than minimum path mtu] -- msa + // + // *NOTE* + // Flows are not notified of this! [hopefully + // the caller will do something about it]. + if (aMtu >= aMin && (iPMtu < aMin || iPMtu > aMtu)) + iPMtu = aMtu; + LOG(Log::Printf(_L("\tIF %u [%S] Proposed MTu=%d, current Send MTU=%d, Recv MTU=%d, Path MTU=%d"), iScope[0], &iName, aMtu, iSMtu, iRMtu, iPMtu)); + } + +// MaskLength +// ********** +// Local utility, compute consecutive leftmost 1-bits from 32 bit integer +// +// Not optimized for speed or anything... +// +static TInt MaskLength(TUint32 aAddr) + { + TInt count = 0; + // obviously, this is "brute force" counting + while (aAddr & 0x80000000) + { + count++; + aAddr <<= 1; + } + return count; + } +static TInt MaskLength(const TIp6Addr &aAddr) + { + TInt count = 0; + TUint loopCount = sizeof(aAddr.u.iAddr8) / sizeof(aAddr.u.iAddr8[0]); + for (TUint i = 0; i < loopCount; ++i) + if (aAddr.u.iAddr8[i] == 0xFF) + count += 8; + else + { + count += MaskLength(aAddr.u.iAddr8[i] << 24); + break; + } + return count; + } + +// CIp6Interface::Update6 +// ********************** +// Configure interface for IPv6 if it supports KSoIfInfo6 +TInt CIp6Interface::Update6(TInt aTransition) + { + if (iIsIPv6) + return aTransition; // Do not redo configuration! + + // Error returns from the following query is an indication + // that the driver does not support IPv6. + TPckgBuf ifProp; + ASSERT(iNifIf != NULL); + if (iNifIf->Control(KSOLInterface, KSoIfInfo6, ifProp) != KErrNone) + return aTransition; // No IPv6 support, exit + + iFeatures = ifProp().iFeatures; + iSpeedMetric = ifProp().iSpeedMetric; + SetMtu(ifProp().iMtu, KInet6MinMtu); + iRMtu = ifProp().iRMtu; + + TPckgBuf cfg; + cfg().iFamily = KAfInet6; + if (iNifIf->Control(KSOLInterface, KSoIfConfig, cfg) != KErrNone) + return aTransition; // No IPv6 support, exit + + iIsIPv6 = 1; // Ok, configure for IPv6 + aTransition = KIfaceTransition_UP; + + if (iFeatures & KIfCanMulticast) + { + CIp6Route *route; + // If interface indicates multicast capability, then add a default + // multicast route for it (to allow join group to select this + // interface!) + route = GetRoute(KInet6AddrAllNodes, 8, KRouteAdd_ONLINK); + if (route && (iFeatures & KIfIsLoopback)) + { + // Multicast routes on loopback interfaces should have poor metric. + // If a "real" network interface comes up, it should be favoured over loopback. + route->iMetric = KLoopbackMcastMetric; + } + } + + if (cfg().iLocalId.Family() != KAFUnspec) + (void)AddId(cfg().iLocalId); + + if (cfg().iRemoteId.Family() != KAFUnspec) + { + // Assume the interface is giving implicitly an address + // of some other host on the link (probably a Point-to-Point + // link, and this is the other end of the link). Just construct + // a link local address for it and setup a host route. + // + TIp6Addr remote(KInet6AddrLinkLocal); + MakeFullAddress(remote, 10, cfg().iRemoteId.Ptr(), cfg().iRemoteId.Length()); + (void)GetRoute(remote, 128, KRouteAdd_ONLINK); + } + + // Initialize name servers from configuration + UpdateNameServers(cfg().iNameSer1, cfg().iNameSer2); + + // + // Add permanent multicast groups + // + (void)GetRoute(KInet6AddrNodeLocal, 128, KRouteAdd_MYPREFIX); +#if 0 + (void)UpdateMulticast(KInet6AddrAllNodes); +#else + if (iScope[1]) // Interface has link local scope id? + (void)UpdateMulticast(KInet6AddrAllNodes); +#endif + return aTransition; + } + +// CIp6Interface::ConfigureAddress +// ******************************* +/** +// Internal utility which configures an first/additional IPv4 +// address + netmask for the interface +// +// @param aAddr +// The IPv4 address in IPv4-mapped format +// @param aMaskLength +// The netmask length (bits counted for IPv6, thus netmask +// is configured only if 96 < aMaskLength <= 128. +// +// @return +// @li = 0, if no change in configuration +// @li = 1, if configuration changed +*/ +TInt CIp6Interface::ConfigureAddress(const TIp6Addr &aAddr, const TUint aMaskLength, const TBool aForcePrimary) + { + ASSERT(aMaskLength <= 128); + if (aMaskLength > 128) + return 0; + +#ifdef _LOG + TLogAddressPrefix tmp(aAddr, aMaskLength); + Log::Printf(_L("\tIF %u [%S] ConfigureAddress([%S])"), iScope[0], &iName, &tmp); +#endif + + // Address can be configured only if there is an address... + if (aAddr.u.iAddr32[3] == 0) + return 0; + + // + // Setup up my own address + // ----------------------- + // Convert TInetAddr iAddress into ipv4 compat address + // and set it up as a prefix and id + + if (AddId(aAddr, 0, TIp6AddressInfo::ENormal, aForcePrimary) == 0) + // No change. + return 0; + + // Setup up netmask (if defined) + // ----------------------------- + if (aMaskLength > 96) + { + // + // Add ONLINK route for the net + // + (void)GetRoute(aAddr, aMaskLength, KRouteAdd_ONLINK); + // + // Make network broadcast address as a "multicast group" into the routes. + // my_net is the network prefix combined with all-ones host part (= broadcast address) + TIp46Addr my_net(0xffffffffU >> (aMaskLength-96)); + my_net.u.iAddr32[3] |= aAddr.u.iAddr32[3]; + CIp6Route *const rt = GetRoute(my_net, 128, KRouteAdd_MYPREFIX); + if (rt) + rt->iIsMulticast = 1; // mark it as "multicast"! + } + return 1; + } + + +// CIp6Interface::FindInternalIpv4LinkLocalAddr +// **************************************** +/** +// Find the one and only internally generated IPv4 link-local, if present. +// +// @return the TIp6AddressInfo, if such address exists; and NULL otherwise. +*/ +TIp6AddressInfo* CIp6Interface::FindInternalIpv4LinkLocalAddr() + { + // Call does not get ownership of object. + return const_cast( FindIpv4LinkLocalAddr() ); + } + + +// CIp6Interface::RandomAddress +// **************************** +// Generate pseudorandom IPv4 link local address. +TInt CIp6Interface::RandomAddress(TIp6Addr &aAddr, TUint aPrefix, TUint aN) + { + // Use current hardware address as a seed and generate N'th variant. + // Optimized for space, not speed (wasting CPU on generating + // the N-1 numbers needlessly, but saving the need to store + // the seed in CIp6Interface...). + // + // *NOTE 1* In current use aN is stored in TUint8 of + // TIp6AddressInfo::iGenerated => after 256 addressesses, + // the same sequence starts, and the loop below does not + // grow into infinity... (in practice aN = 0 or 1) + // *NOTE 2* To avoid the loop, one would need to store + // two seeds into CIp6Interface, one for IPv4 and one for + // IPv6. + // + // The use of hw as seed gives the effect that host tends to + // get the same link local address, if possible + TInt64 seed = (TInt64 &)iHwAddr[8]; + TReal r; + TUint i = 0; + do + r = Math::FRand(seed); + while (++i <= aN); // pick N'th pseudo-random number + + + if (aAddr.IsV4Mapped()) + { + if (aAddr.Scope() != KIp6AddrScopeLinkLocal) + return 0; // IPv4 address can only be generated if Link Local + + TReal random_addr_float; + TInt32 random_addr = 0; + + (void)Math::Round(random_addr_float, r * (INET_ADDR(169,254,254,255) - INET_ADDR(169,254,1,0)), 0); + (void)Math::Int(random_addr, random_addr_float); + TIp46Addr addr(INET_ADDR(169,254,1,0) + random_addr); + + aAddr = addr; + return 1; + } + // + // For IPv6, a placeholder for now -- just use the current seed as a source for the ID part + // (this part is not used until privacy, RFC-3041 is implemented) + // + MakeFullAddress(aAddr, aPrefix, (TUint8 *)&seed, sizeof(seed)); + return 1; + } + +// CIp6Interface::DuplicateAddress +// ******************************* +// The specified address has been detected as duplicate. +void CIp6Interface::DuplicateAddress(TIp6AddressInfo *aId, TBool &aDefendIPAddress, TBool aGratuitousArp) + { + if (aId == NULL) + return; + TIp6Addr addr = aId->iId; + for (;;) + { + TTime stamp; + stamp.UniversalTime(); + + if (!aId->IsTentative()) + { + // + // Messy situation, a collision on established address. The + // following logic is applied: if address is younger than + // DupAddrDefendTime seconds, give it up. Otherwise defend + // address by sending an announcement. However, to prevent looping + // on this, reset creation time of the address to now. + // + TLifetime now = ElapsedUnits(aId->iCreated, stamp); + if (now > CIp6Manager::TimerUnits(iND.iDupAddrDefendTime)) + { + if(aGratuitousArp) + { + aDefendIPAddress = ETrue; + } + else + { + // Old established address, try to keep it: reset + // creation time and send an announcement for it... + aId->iCreated = stamp; + (void)SendNeighbors(KInet6ICMP_NeighborSol, NULL, aId->iId); + } + return; + } + } + // + // A tentative address or an established address which we + // going to give up... + // + if (aId->iGenerated == 0) + break; // Not automatically generated or has been generated + // too many times already! + if (!RandomAddress(addr, aId->iPrefix, aId->iGenerated)) + break; // failed for some reason + // + // A new address has been generated + // + aId->iGenerated++; + SetId(*aId, addr, aId->iPrefix, aId->AddressType()); + if (aId->iGenerated >= iND.iMaxAddrRegenerations) + // If we have exceeded the limitation of regenerations, + // then put the creation time 60sec into future, and thus + // delay the activation of this address (probes start + // at least 60s delayed). + aId->iCreated += TTimeIntervalSeconds(60); + // + // Start the DAD process + // + Timeout(stamp); + return; + } + // + // No new address, remove the duplicate + // + RemId(aId); + } + +// CIp6Interface::ConfigureLinkLocal +// ********************************* +/** +// Internal utility for automatic configuring of the linklocal IPv4 address. +// +// @param aConfAddr IPv4 address received from CNifIfBase::Control(). If 0, no IPv4 address +// was configured on Nif, and linklocal address will be enabled on settings +// 2 (EV4LLConditional) and 1 (EV4LLAlways). +// +// @return +// @li = 0, if no change in configuration +// @li = 1, if configuration changed +*/ +TInt CIp6Interface::ConfigureLinkLocal(TUint32 aConfAddr) + { + // IPv4 link local specification applies only for + // interfaces that support Neighbour Discovery). + if (!NeedsND()) + return 0; + + // Always support IPv4 LL on all ND interfaces, by + // always installing the IPv4 LL onlink route. + const TInt prefix = 96+16; // Ipv4-mapped format, need to add 96 + TIp46Addr addr(KInetAddrLinkLocalNet); + if (GetRoute(addr, prefix, KRouteAdd_ONLINK) == NULL) + { + // If this route cannot be created or does not exist, + // there is no point in doing anything else here. + return 0; + } + + // Check if automatic configuration has already been done, + TIp6AddressInfo *const exists = FindInternalIpv4LinkLocalAddr(); + + // Currently, detecting duplicates is only defined for + // interfaces that support ARP, and this is only possible if + // there are link layer addresses. + // => LinkLocals can only be generated on interface + // which has addresses! + const TInt flag = HaveIp4LinkLocal(); + if (flag == EV4LLDisabled || + (flag == EV4LLConditional && aConfAddr) || + iHwAddr.Family() == KAFUnspec) + { + // The automatically configured IPv4 should not exist - remove + // if it does. RemId can be called with NULL, and returns + // KErrNone, if address was actually removed. + TInt retVal = RemId(exists); + + #ifdef _LOG + if( retVal == KErrNone ) + { + TBuf<39> addrStr; + + TInetAddr( addr, 0 ).Output( addrStr ); + + Log::Printf( _L( "CIp6Interface::ConfigureLinkLocal - Link local address %S removed" ), &addrStr ); + } + #endif + + return retVal; + } + + if (exists) + { + // Just reset the lifetimes, in case it was in deprecated status + exists->iPLT = KLifetimeForever; + exists->iVLT = KLifetimeForever; + return 0; + } + // + // Address does not exist yet - make it unless we are trying to reuse + // an old address. + // + if( RandomAddress(addr, prefix, 0) && ConfigureAddress( addr, prefix ) ) + { + // If address generated, must find the address and + // mark it as generated. + TIp6AddressInfo *const id = GetId(addr); + if (id) + { + id->iIpv4LinkLocal = 1; // Mark it as Internally Generated IPv4 LL. + if (id->iGenerated == 0) + id->iGenerated = 1; + // Add a random constant to the creation time, so that DAD starts after a random + // delay. [timers 1sec accuracy is a bit problem here -- msa] + ASSERT(iND.iIPv4RetransTimer < 2000); // ensures non-negative adjust below. + id->iCreated += TTimeIntervalMicroSeconds32((TInt)(Math::FRand(Interfacer().iSeed) * iND.iIPv4RetransTimer * 1000000.0)); + + #ifdef _LOG + TBuf<39> addrStr; + + TInetAddr( addr, 0 ).Output( addrStr ); + + Log::Printf( _L( "CIp6Interface::ConfigureLinkLocal - Link local address %S configured" ), &addrStr ); + #endif + } + return 1; + } + return 0; + } + +CIp6Route *CIp6Interface::StartProbeND(const TIp6Addr &aSrc, const TIp6Addr &aDst) + /** + * Probe for an address on the link. + * + * Starts a probing neighbour discovery on a destination address. + * This can be used to force ND on any address. + * + * @param aSrc Source address to be used in probing + * @param aDst Destination to probe + * @return + * Host route entry, if probing started (or was already active). + * Or, NULL not started. + */ + { + CIp6Route *const n = GetRoute(aDst, 128, KRouteAdd_PROBINGONLY); + if (n && n->iIsProbing) + { +#ifdef _LOG + TLogAddressPrefix dst(aDst); + TLogAddressPrefix src(aSrc); + Log::Printf(_L("\tIF %u [%S] StartProbeND(src=%S, dst=%S)"), iScope[0], &iName, &src, &dst); +#endif + n->StartND(aSrc); + return n; + } + return NULL; + } + + + +// CIp6Interface::UpdateNameServers +// ******************************** +/** +// Internal utility to load the namer server addresses consistently +// @param ns1 The name server address 1. +// @param ns2 The name server address 2. +// @param aOverride +// @li == 0 => addresses are only changed if unspecified previously +// @li != 0 => new specified address always overwrites previous setting +*/ +void CIp6Interface::UpdateNameServers(const TInetAddr &ns1, const TInetAddr &ns2, const TInt aOverride) + { +#ifdef _LOG + TLogAddressPrefix old_ns(ns1); + TLogAddressPrefix new_ns(ns2); + Log::Printf(_L("\tIF %u [%S] UpdateNameServers(ns1=%S, ns2=%S, override=%d)"), iScope[0], &iName, &old_ns, &new_ns, aOverride); + old_ns.Set(iNameSer1); +#endif + // + // 1. name server address + // + if (ns1.Family() != KAFUnspec && (aOverride || iNameSer1.Family() == KAFUnspec)) + { + if (ns1.IsUnspecified()) + iNameSer1.Init(KAFUnspec); + else + iNameSer1 = ns1; + } +#ifdef _LOG + new_ns.Set(iNameSer1); + Log::Printf(_L("\tIF %u [%S] ns1: old=%S new=%S"), iScope[0], &iName, &old_ns, &new_ns); + old_ns.Set(iNameSer2); +#endif + // + // 2. name server address + // + if (ns2.Family() != KAFUnspec && (aOverride || iNameSer2.Family() == KAFUnspec)) + { + if (ns2.IsUnspecified()) + iNameSer2.Init(KAFUnspec); + else + iNameSer2 = ns2; + } +#ifdef _LOG + new_ns.Set(iNameSer2); + Log::Printf(_L("\tIF %u [%S] ns2: old=%S new=%S"), iScope[0], &iName, &old_ns, &new_ns); +#endif + } + +// +// CIp6Interface::Update4 +// ********************** +// Configure interface for IPv4 if it supports KSoIfInfo and KSoIfConfig +TInt CIp6Interface::Update4(TInt aTransition) + { + if (iIsIPv4) + return aTransition; // Do not redo configuration, if it has been already done! + + TPckgBuf info_buf; + ASSERT(iNifIf != NULL); + TInt err = iNifIf->Control(KSOLInterface, KSoIfInfo, info_buf); + if (err != KErrNone) + return aTransition; // No IPv4 support (no change) + // + // Basic minimal configuration + // + + const TSoIfInfo &info = info_buf(); + iFeatures = info.iFeatures; + iSpeedMetric = info.iSpeedMetric; + SetMtu(info.iMtu, KInetMinMtu); + iRMtu = info.iMtu; // In IPv4 there is no separate slot for + // receive and send MTU (assume they are same) + + // Need to magically setup routing for the IPv4 interfaces, + // just ask the interface parameters and make best effort... + TPckgBuf cfg; + if ((err = GetIp4Config(iNifIf, cfg)) != KErrNone) + return aTransition; // No IPv4 support + + // For all IPv4 Interfaces, setup 255.255.255.255 address + static const TIp6Addr broadcast = {{{0,0,0,0,0,0,0,0,0,0,0xff,0xff,255,255,255,255}}}; + CIp6Route *const rt = GetRoute(broadcast, 128, KRouteAdd_MYPREFIX); + if (rt) + rt->iIsMulticast = 1; // mark it as "multicast" + + // For all IPv4 Interfaces: join to 224.0.0.1 multicast group (all hosts) + static const TIp6Addr mc_hosts = {{{0,0,0,0,0,0,0,0,0,0,0xff,0xff,224,0,0,1}}}; + (void)UpdateMulticast(mc_hosts); + if ((iFeatures & (KIfCanMulticast|KIfIsLoopback)) == KIfCanMulticast) + { + // Add default IPv4 multicast route (but not on loopbacks!) + // (this puts all multicast as "ONLINK", instead of possibly + // punting them to the default gateway!) + (void)GetRoute(mc_hosts, 100, KRouteAdd_ONLINK); + } + + // cfg().iConfig values: + // + const TInetIfConfig &cf = cfg().iConfig; + const TUint32 addr = cf.iAddress.Address(); + const TIp46Addr my_addr(addr); + + // Initialize name servers from configuration + UpdateNameServers(cf.iNameSer1, cf.iNameSer2); + + // Configure "configured" address, if any, and configure ZEROCONF link local + // address (if not already done). Only the EV4LLAlways and EV4LLConditional + // with no static IP address options cause link local creation at this time. + // A configuration daemon may create a link local at its discretion (e.g., + // if DHCP discovery fails) if the EV4LLConfigDaemonControlled option + // is enabled. + TInt changed = ConfigureAddress(my_addr, 96+MaskLength(cf.iNetMask.Address())); + const TInt flag = HaveIp4LinkLocal(); + if( flag != EV4LLConfigDaemonControlled ) // daemon with EV4LLConfigDaemonControlled interface option will use KSoInetCreateIPv4LLOnInterface socket option to configure a link local if desired + { + changed |= ConfigureLinkLocal( addr ); + } + if (changed == 0) + return aTransition; // -- no change in update4 + + iIsIPv4 = 1; // Freeze current configuration and mark IF as IPv4 capable + + const TUint32 defgate = cf.iDefGate.Address(); + if (defgate) + { + const TIp46Addr tmp(defgate); // tmp required to get it compiled with gcc! + if (defgate == addr) + { + // *NOTE* Apparently some GPRS phones have a PPP server which gives + // my own address as a default gateway, do not add gateway route + // in such case (it might confuse next hop selection). + // + // *NOTE* This branch is only entered when the interface reports + // "broken/bad" configuration information (unless we define the + // condition "gateway == my address" to mean exactly this: install + // default IPv4 onlink route to the link). + (void)GetRoute(tmp, 96, KRouteAdd_ONLINK); + } + else + { + const TInetAddr gateway(tmp, 0); + (void)GetRoute(tmp, 128, KRouteAdd_ISROUTER); + (void)GetRoute(tmp, 96, KRouteAdd_GATEWAY, &gateway); + } + } +#if 0 // iBrdAddr appears to hold the other end address?? + + // TInetAddr iBrdAddr. Store net broadcast address into + // iPrefix[0]... (so it will be recognized as own...) + cf.iBrdAddr.ConvertToV4Mapped(); + iPrefix[0] = TIp6Prefix(cf.iBrdAddr.Ip6Address(), 128); +#endif + + return KIfaceTransition_UP; + } + +// CIp6Interface::SendNeighbors +// **************************** +const TInt KSendNeighbors_NO_OVERRIDE = 0x100; // Do not set OVERRIDE bit in NA +/** +// Send Neighbor Discovery packets (IPv6 ND or IPv4 ARP). +// +// Internal help method which is used to send send one of the +// following +// @li Neighbor Solicitation +// @li Neighbor Advertisement +// @li Router Solicitation +// +// to the interface +// +// @param aMessageType +// The low 8 bits are the ICMP6 type code of the message to be sent +// The higher bits can be used as flags for details +// @param aDest +// The (host) route to be used in sending. This is used for unicast +// ND traffic. If NULL, then packet will have multicast destination. +// @param aTarget +// The target address (used in NS/NA). +// @param aSource +// The source address to use. If not given (NULL or unspecified), the source is +// selected by the destination (for ND) or by the aTarget (for ARP) address. +// @return +// @li == KErrNone, if send apparently succeeded +// @li != KErrNone, for problems detected (out of memory mostly) +// +// WARNING: +// This code is "hand tailored" to work exactly and *ONLY* with the listed +// types of ICMP messages. If a support for a new type of ICMP is to be +// added, the code must be reviewed very carefully! -- msa +*/ +TInt CIp6Interface::SendNeighbors(TInt aMessageType, CIp6Route *aDest, const TIp6Addr &aTarget, const TIp6Addr *const aSource) + { +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::SendNeighbors()"))); + #endif +#endif // SYMBIAN_TCPIPDHCP_UPDATE + const TUint8 icmp_type = (TUint8)aMessageType; + RMBufSendPacket packet; + RMBufSendInfo *info = NULL; + TInt err = KErrNone; + TIp6Addr dst, src; + +#ifdef ARP + // If the target is IPv4 address, then translate the IPv6 ND request to + // ARP message. + if (aTarget.IsV4Mapped()) + { + // The ARP kludge needs only to map the Neighbor Solicitation + // into ARP Request (this really should not get called with + // anything else). + if (icmp_type != KInet6ICMP_NeighborSol) + return KErrNone; + + // + // Source address is actually used in the ARP packet and + // must thus match the ARP target. Thus, select it by + // target. + if (aSource && (aSource->u.iAddr32[3] == 0 || IsMyAddress(*aSource))) + src = *aSource; + else if (SelectSource(src, aTarget) == NULL) + src = KInet6AddrNone; + + const TUint arp_length = TInet6HeaderArp::MinHeaderLength() + + (iHwAddr.GetUserLen() + 4) * 2; + TRAP(err, info = packet.CreateL(arp_length)); + if (err != KErrNone || info == NULL) + return err; + for (;;) + { + TInet6Packet arp; + arp.Set(packet, 0, arp_length); + if (arp.iHdr == NULL) + break; + arp.iHdr->SetPrAddrLen(4); + arp.iHdr->SetHwAddrLen(iHwAddr.GetUserLen()); + // Assume the required ARP Hardware type is returned in the port + // field of the hardware address of the interface (either this, + // or the interface snoops ARP and fixes the value for this + // field.. -- msa) + arp.iHdr->SetHardwareType(iHwAddr.Port()); + arp.iHdr->SetProtocolType(KArpProtocolType_IP); + arp.iHdr->SetOperation(EArpOperation_REQUEST); + arp.iHdr->SenderHwAddr().Copy(iHwAddr.Address()); + arp.iHdr->TargetHwAddr().FillZ(); + // Assume src & target are IPv4 mapped address... + arp.iHdr->SenderPrAddr().Copy(TPtrC8(&src.u.iAddr8[12], 4)); + arp.iHdr->TargetPrAddr().Copy(TPtrC8(&aTarget.u.iAddr8[12], 4)); + info->iProtocol = KProtocolArp; + info->iFlags = 0; + // Note: if aDest is non-NULL, then this iDstAddr will be + // will be replaced in aDest->Send() with the link layer + // address... -- msa + TInetAddr::Cast(info->iDstAddr).SetAddress(KInetAddrBroadcast); + TInetAddr::Cast(info->iSrcAddr).SetAddress(0); // Don't care + packet.Pack(); + // draft-ietf-zeroconf-ipv4-linklocal-05.txt says that whenever + // the sender is ipv4 link local, then the replies (and requests) + // must always be sent to the broadcast address [IMHO, this is + // a bit dubious rule, but if it is so specified, comply... -- msa] + if (aDest && src.Scope() != KIp6AddrScopeLinkLocal) + aDest->Send(packet); + else if (iState == EFlow_READY) + { + // Send only if ready, to avoid queuing ARP packets into hold queue. + Send(packet, NULL); + } + break; + } + packet.Free(); + return KErrNone; + } +#endif + // + // If destination is Unspecified, use solicited node address generated from + // the target address (as first default, may be changed later below) + // + if (aDest) + dst = aDest->iPrefix; + else + dst = (const TIp6Addr)TSolicitedNodeAddr(aTarget); + // + // Try to pick aSrc address, if not specified by the caller + // Leave it unspecified, if no valid source addresses. + // + if (aSource && (aSource->IsUnspecified() || IsMyAddress(*aSource))) + // However, a source address must be a valid address on this interface + // or unspecified (it cannot be a proxy or anycast). Thus, IsMyAddress + // test in above. [This situation occurs when node is acting as + // a router/proxy and is trying to find destination cache for a + // forwarded packet, by normal rules the source is taken from the + // packet. + // Could perhaps do this test before calling SendNeighbors? --msa] + src = *aSource; + else if (SelectSource(src, dst) == NULL) + src = KInet6AddrNone; + // This allocates too much space for the RS, but as class + // used in TInet6Checksum is NA, the mapping would fail + // for too short RMBufChain... icky! -- msa + // [but, as one RMBuf is always needed anyway, it doesn't + // cause any real extra allocations either...] + TUint icmp_length = TInet6HeaderICMP_NeighborAdv::MinHeaderLength(); + for (;;) // for handy error exits via breaks... + { + TInt link_layer = 0; // The length of the SLL/TLL option in bytes + if (iHwAddr.Family() != KAFUnspec && !src.IsUnspecified()) + { + // Got Link Layer Address, include it into the solicitation + link_layer = ((iHwAddr.GetUserLen() + 2) + 7) & ~0x7; + icmp_length += link_layer; + } + + TRAP(err, info = packet.CreateL(icmp_length)); + if (err != KErrNone || info == NULL) + break; + + ASSERT((TUint)info->iLength == icmp_length); + + TInet6Checksum icmp(packet); + if (icmp.iHdr == NULL) + break; // Shouldn't happen! + // + // Build the ICMP Message + // + icmp.iHdr->SetType(icmp_type); + icmp.iHdr->SetCode(0); + icmp.iHdr->SetParameter(0); // (for NA, this may contain flags, see below) + switch (icmp_type) + { + case KInet6ICMP_NeighborAdv: +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::SendNeighbors() KInet6ICMP_NeighborAdv is called"))); + #endif +#endif // SYMBIAN_TCPIPDHCP_UPDATE + // Make some guesses for S and O bits of NA (these + // depend on how this SendNeighbors method is called + // in the current implementation!) -- msa + // + if (aDest) + { + // Assume NA to a specific destionation + // is always SOLICITED! + icmp.iHdr->SetS(1); + } + else + { + // Otherwise dest is allways for all nodes + dst = KInet6AddrAllNodes; + } + if ((KSendNeighbors_NO_OVERRIDE & aMessageType) == 0) + { + // By default all NA's are for own address, so O=1, if we + // have link_layer addr (unless disabled by the caller). + // (NA's proxy addresses must not have O set, for example) + icmp.iHdr->SetO(link_layer); + } + icmp.iHdr->SetR(iIsRouter); + // *FALL TRHOUGH TO NS*/ + case KInet6ICMP_NeighborSol: +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::SendNeighbors() KInet6ICMP_NeighborSol is called"))); + #endif +#endif // SYMBIAN_TCPIPDHCP_UPDATE + icmp.iHdr->Target() = aTarget; // NS & NA have same format for this + break; + case KInet6ICMP_RouterSol: + if (aDest == NULL) + // Unspecific destination is always to all ROUTERS + dst = KInet6AddrAllRouters; + // Argh! because we allocated too much space for the + // buffer, the info->iLength is now incorrect for RS. + // Need to fix.. another yech! -- msa + icmp_length -= + TInet6HeaderICMP_NeighborAdv::MinHeaderLength() - + TInet6HeaderICMP_RouterSol::MinHeaderLength(); + packet.TrimEnd(icmp_length); + break; + default: + ASSERT(0); + break; + } + // + // Add Target (for NA) or Source (for NS and RS) Link-layer address option (if possible) + // + if (link_layer > 0) + { + const TPtr8 ptr(((RMBufPacketPeek &)packet).Access(link_layer, info->iLength - link_layer)); + if (ptr.Length() < link_layer) + break; + TInet6OptionICMP_LinkLayer *addr = (TInet6OptionICMP_LinkLayer *)ptr.Ptr(); + addr->SetType(icmp_type == KInet6ICMP_NeighborAdv ? KInet6OptionICMP_TargetLink : KInet6OptionICMP_SourceLink); + addr->SetLength(link_layer >> 3); // Option length is in units of 8 octets! + addr->Address().Copy(iHwAddr.Address()); + } + // + // Complete the Info structure for ICMP Checksum computation + // + info->iProtocol = KProtocolInet6Icmp; + TInetAddr::Cast(info->iDstAddr).SetAddress(dst); + TInetAddr::Cast(info->iSrcAddr).SetAddress(src); + TInetAddr::Cast(info->iDstAddr).SetScope(iScope[dst.Scope()-1]); // scopeid from current interface + + // Create unconnected flow context to the packet (info) + if (info->iFlow.Open(iNifUser->iNetwork, info->iProtocol) != KErrNone) + break; + CIp6Flow *flow = (CIp6Flow *)info->iFlow.FlowContext(); + if (!flow) + break; + // Setup the connection information and connect + info->iFlow.SetRemoteAddr(info->iDstAddr); + info->iFlow.SetLocalAddr(info->iSrcAddr); + info->iFlow.SetIcmpType(icmp_type, 0); + flow->iInfo.iLocalSet = 1; // Disable source address select (even for unspecified) + flow->iInfo.iLockId = iScope[0]; // Accept connect only to this interface. + flow->iInfo.iLockType = EScopeType_IF; // Accept connect only to this interface. + flow->iOptions.iMulticastHops = 255; // ND wants hoplimit = 255 for multicast + flow->iOptions.iHopLimit = 255; // ND wants hoplimit = 255 for unicast + flow->iOptions.iMulticastLoop = 0; // We don't want to see own packets! + flow->iOptions.iKeepInterfaceUp = 0; // We don't keep IF up just for ND traffic. + info->iFlow.Connect(); + if (flow->iStatus != KErrNone) + { + LOG(Log::Printf(_L("\tIF %d [%S] SendNeighbors connect failed (%d)"), iScope[0], &iName, (TInt)flow->iStatus)); + info->iFlow.Close(); + break; // Cannot get a flow opened.. + } + // The "assert" below might seem logical, but it is not, if ND is + // to IPSEC VPN interface (VPN != Real interface for flow). + // -- thus, remove it! + // __ASSERT_DEBUG(iNifIf == info->iFlow.Interface(), User::Panic(_L("DEBUG"), 0)); + + info->iFlags = 0; + icmp.ComputeChecksum(packet, info); + packet.Pack(); + (void)iNifUser->iNetwork->Send(packet, NULL); + LOG(Log::Printf(_L("<>\tCIp6Interface::SendNeighbors() KInet6ICMP_NeighborSol is connected"))); + return KErrNone; + // + } // <-- Never get here, not a real loop! + packet.Free(); + LOG(Log::Printf(_L("\tIF %u [%S] SendNeighbors send failed (%d)"), iScope[0], &iName, err)); + return err < 0 ? err : KErrNoMemory; + } + + +// +// CIp6Interface::StartSending +// *************************** +// Interface specific StartSending method. Generic code (and IPv6 stuff) +TInt CIp6Interface::StartSending() + { + if (!iNifIf) + return KErrGeneral; // Should never happen!? + + // Getting a StartSending from a device means that the driver + // is ready for the first or more input. Thus, by default set + // the interface state initially to EFlow_READY. The code + // after this may decide to set some other state later. + // + // Similarly, decide on initial return value for the + // transition state + TInt transition = (iState > 0) ? KIfaceTransition_READY : KIfaceTransition_NONE; + iState = EFlow_READY; + + // + // If there are any packets in hold queue, then flush out + // as many as possible now... + // + if (!iHoldQueue.IsEmpty()) + { + RMBufChain packet; + TInt count = 0; + while (iHoldQueue.Remove(packet)) + { + count++; + // Use the standard Send() method! This has a code to re-insert the packet + // into hold queue (but, it will not fire as long as iState is not + // EFlow_HOLD!) + (void)Send(packet); + if (iState > 0) + { + // The interface went back to hold, ignore start sending + LOG(Log::Printf(_L("\tIF %u [%S] NIF signals HOLD after sending %d packets from hold queue (%d)"), + iScope[0], &iName, count, (TInt)iHoldQueue.IsEmpty())); + return KIfaceTransition_NONE; + } + } + LOG(Log::Printf(_L("\tIF %u [%S] Flushed hold queue (%d) successfully"), iScope[0], &iName, count)); + } + + // Configure Network and IAP identifiers + TPckgBuf netinfo; + if (iNifIf->Control(KSOLInterface, KSoIfGetConnectionInfo, netinfo) == KErrNone) + { + // NIF supports Network Information + LOG(Log::Printf(_L("\tIF %u [%S] has IAP=%d, NET=%d"), + iScope[0], &iName, (TInt)netinfo().iIAPId, (TInt)netinfo().iNetworkId)); + } + else + { + // NIF does not support Network Information, pick some dummies + netinfo().iIAPId = ~iScope[0]; + netinfo().iNetworkId = KDefaultNetworkId; + LOG(Log::Printf(_L("\tIF %u [%S] has no ConnectionInfo, defaulting IAP=%d, NET=%d"), + iScope[0], &iName, (TInt)netinfo().iIAPId, (TInt)netinfo().iNetworkId)); + } + // + // Initialize the scope vector from netinfo + // + iScope[1] = netinfo().iIAPId; // - Link Local Scope (2) + iScope[2] = netinfo().iIAPId; // - Subnet-local Scope (3) + // Remaining slots will get the network id + for (TInt i = 3; i <= EScopeType_NET; ++i) + iScope[i] = netinfo().iNetworkId; + + // + // Refresh the hardware address of the interface on each StartSending + // (if link layer addresses are supported by the interface) + // + TPckgBuf hwaddr; + if (iNifIf->Control(KSOLInterface, KSoIfHardwareAddr, hwaddr) == KErrNone) + iHwAddr = TLinkAddr::Cast(hwaddr().iHardwareAddr); + else + iHwAddr.SetFamily(KAFUnspec); + + transition = Update4(transition); + transition = Update6(transition); + + if (transition == KIfaceTransition_UP) + { + // + // Save the UP transition time into the iAddress.iPreferredLifetime + // and activate router finding and duplicate address detection. + // + // + // Before sending the solicitations, choose a delay [0..MAX_RTR_SOLICITATION_DELAY] + // + const TInt delay = (TInt)(Math::FRand(Interfacer().iSeed) * iND.iMaxRtrSolicitationDelay * 1000000.0); + iAddress.iCreated += TTimeIntervalMicroSeconds32(delay); + LOG(Log::Printf(_L("\tIF %u [%S] Next event delay=%d [us]"), iScope[0], &iName, delay)); + iAddress.iNS = 0; + iRetryRS = 0; + Interfacer().SetTimerWithUnits(iTimeout, CIp6Manager::TimerUnits(delay, 1000000)); + + // This is treated as a change-type event, the add event is considered to occur + // in DoBind (i.e. when InterfaceAttached is called) + NotifyInterfaceEvent(EventTypeModify); + } + + return transition; + } + + +void CIp6Interface::NotifyInterfaceEvent(TUint aEventType) const +{ + CIp6Manager *const mgr = &Interfacer(); + + // If there is no event manager, or if there are no registered listeners, we can exit + // the function right away + if (!mgr->EventManager()) + return; + + if (mgr->EventManager()->IsEmpty(EClassInterface)) + return; + + TInetInterfaceInfo info; + + info.iIndex = iScope[0]; + info.iHwAddr = iHwAddr; + info.iName = iName; + info.iFeatures = iFeatures; + info.iSMtu = iSMtu; + info.iRMtu = iRMtu; + info.iSpeedMetric = iSpeedMetric; + + // Copied and edited from interfaceinfo() + if (iNifIf == NULL) + info.iState = EIfDown; // no interface or address not known or was duplicate + else if (iState == EFlow_READY) + info.iState = EIfUp; + else if (iState == EFlow_PENDING) + info.iState = EIfPending; + else if (iState == EFlow_HOLD) + info.iState = EIfBusy; +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + else if (iState == EFlow_NOTCONFIGURE) + info.iState = EIfNotConfigured; +#endif //SYMBIAN_TCPIPDHCP_UPDATE + else + info.iState = EIfDown; + + mgr->EventManager()->Notify(EClassInterface, aEventType, &info); +} + + +// +// CIp6Interface::Timeout() +// ************************ +// +void CIp6Interface::Timeout(const TTime &aStamp) + { + // Somewhat twisted logic for sending RS and NS, but not wanting to run + // separate timers for both, and as they have different transmit intervals, + // the decision whether to send RS/NS or not, is tricky... -- msa + // + // The process starts when interface does the UP transition (see StartSending), + // at that point the start time of the process is saved into iAddress.iCreated + // + // If (transmitted_packets * transmit_interval <= elapsed) + // a packet can be sent; + // + // Timeout can be called more often than packets are sent, but only after sufficient + // amount of time has passed, the actual sending occurs. [If there is a configuration + // error, and either transmit_interval is ZERO, then those packets are sent back to + // back without delay (causing *recursive* calls to this Timeout()!)]. + // +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::Timeout()"))); + #endif +#endif // SYMBIAN_TCPIPDHCP_UPDATE + TUint next_event = KMaxTUint; + // + // Go through all Address blocks + // + TIp6AddressInfo *privacy = NULL; + TInt have_address = 0; + for (TIp6AddressInfo *id = &iAddress;;) + { + if (id->IsSet()) + { + TUint elapsed_units = ElapsedUnits(id->iCreated, aStamp); +#ifdef _LOG + TLogAddressPrefix tmp(id->iId, id->iPrefix == 0 ? 128 : id->iPrefix); + Log::Printf(_L("\tIF %u [%S] ADDRESS %S %S%S age=%u [1/%d s] PLT=%u VLT=%u"), + iScope[0], &iName, &tmp, &id->LogAddressType(), &id->LogAddressState(), elapsed_units, TIMER_UNIT, id->iPLT, id->iVLT); +#endif + // Some timers and values are different for IPv4 link local + const TInt is_ip4_local = id->iId.IsV4Mapped() && id->iId.Scope() == KIp6AddrScopeLinkLocal; + const TUint retrans_timer = is_ip4_local ? CIp6Manager::TimerUnits(iND.iIPv4RetransTimer) : iRetransTimer; + const TUint dup_transmits = is_ip4_local ? iND.iIPv4DupAddrDetectTransmits : iND.iDupAddrDetectTransmits; + have_address = 1; // ..although, it may be tentative! + if (aStamp < id->iCreated) + { + // Can only happen when iCRT is set to future (may happen with delay > 0 setting) + const TUint time = ElapsedUnits(aStamp, id->iCreated); + if (next_event > time) + next_event = time; + } + else + { + // + // goto's used, sorry. It's just simpler this way (without replicating code) + // + TUint time = id->iNS * retrans_timer; + if (id->IsTentative()) + { + if (id->iNS == 0) + { + // This branch is to "fix" sluggish RunL() calls. When a timeout is scheduled + // the call to RunL gets sometimes delayed and CRT < Current Time. If this is + // the first DAD NS, adjust CRT to the current real time. + id->iCreated = aStamp; + elapsed_units = 0; + } + // + // Duplicate Address Detection + // + // Time to send another NS? + if (time <= elapsed_units) + { + if (id->iNS < dup_transmits) + { + id->iNS++; + LOG(Log::Printf(_L("\tIF %u [%S] Sending %d. NS for %S"), iScope[0], &iName, id->iNS, &tmp)); + // note: source address is forced to be NONE! + (void)SendNeighbors(KInet6ICMP_NeighborSol, NULL, id->iId, &KInet6AddrNone); + time = retrans_timer; // schedule next event at retrans timer + } + else + { + id->SetInitial(0); // Done! + + // Generate an event indicating DAD is complete (iState has changed) + NotifyAddressEvent(EventTypeModify, id->iId, id->iPrefix, NULL, *id); + + // Because missing source address punts flows to holding, + // need to do the ScanHoldings, instead of notifying just + // flows on current interface... -- msa (see RefreshFlow) + Interfacer().ScanHoldings(); + goto announce_test; // Address ready, do the announce test! + } + } + else + time -= elapsed_units; // compute the remaining wait time. + if (next_event > time) + next_event = time; + goto expiration_test;// Address not ready yet, skip over announcement test! + } + +announce_test: // Need to send IPv4 (link local) announcements? + if (is_ip4_local && NeedsND()) + { + // + // For IPv4 link local addresses, send 2 extra "announcements" after address has been accepted + // + if (id->iNS < dup_transmits + iND.iIPv4DupAddrAnnouncements) + { + // Time to send another announcement? + if (time <= elapsed_units) + { + id->iNS++; + LOG(Log::Printf(_L("\tIF %u [%S] Sending %d. NS (announce) for %S"), iScope[0], &iName, id->iNS, &tmp)); + (void)SendNeighbors(KInet6ICMP_NeighborSol, NULL, id->iId, &id->iId); + time = retrans_timer; + } + else + time -= elapsed_units; + if (next_event > time) + next_event = time; + } + } + + // Just remember one id/address block for which lifetime processing + // is required (don't want to it inside the loop, because it may + // require removal and/or addition of new entries, and the loop would + // need to be more complex... -- msa) +expiration_test: + if (id->iPLT < elapsed_units) + privacy = id; + else if (id->iPLT != KLifetimeForever) + { + const TUint time = id->iPLT - elapsed_units; + if (time < next_event) + next_event = time; + } + + if (id->iVLT <= elapsed_units) + privacy = id; + else if (id->iVLT != KLifetimeForever) + { + const TUint time = id->iVLT - elapsed_units; + if (time < next_event) + next_event = time; + } + } + } + if (id->iNext == NULL) + break; + id = &id->iNext->iInfo; + } + if (privacy) + { + const TLifetime current_age = ElapsedUnits(privacy->iCreated, aStamp); + // At most one entry handled at each Timeout(). As these lifetimes should be + // relatively long, anything missed on current pass, will get handled on the + // next round! + if (privacy->iPLT < current_age) + { + // If RFC-3041 is being supported and this is randomly generated id + // for that purpose, one should at least now (but preferrably already + // earlier) regenerate a new random id! This id may still have valid + // life left, so it continues to be used with old connections... + if (privacy->iPrefix == 0) + { + SetPrefix(privacy->iId, 128, 1, KLifetimeForever, 0); + } + } + if (privacy->iVLT < current_age) + RemId(privacy); + } + // Should only be sending RS, if interface as at least one address + // (there is no point in sending RS, if all addresses have become + // disabled by DAD). Also, needs to be IPv6 enabled interface. + // [Even if there are routers, must at least send one RS and get + // one reply after such RS] + if (have_address && CanSendRS()) + { + // + // Try to find if routers are available + // + if (iRetryRS < iND.iMaxRouterSolicitations) + { + const TUint interval = CIp6Manager::TimerUnits(iND.iRtrSolicitationInterval); + // The router discovery "borrows" the creation time of the + // primary address... + TUint time = iRetryRS * interval; + if (iAddress.iCreated > aStamp) + { + // Creation time in future, need to wait... + time = ElapsedUnits(aStamp, iAddress.iCreated); + } + else + { + const TUint elapsed = ElapsedUnits(iAddress.iCreated, aStamp); + if (time <= elapsed) + { + iRetryRS++; + LOG(Log::Printf(_L("\tIF %u [%S] Sending %d. RS"), iScope[0], &iName, iRetryRS)); + // note: src address is not specified: it can be either none or some valid address + // of the interface! + (void)SendNeighbors(KInet6ICMP_RouterSol, NULL, KInet6AddrNone); + time = interval; + } + else + time -= elapsed; + } + if (next_event > time) + next_event = time; + } + } + if (NeedsND()) + { + // An experimental code for cleaning out excess + // unused neighbor cache entries: pick the one + // with oldest reachable confirmation and if + // the time elapsed is long enough, delete it. + // + // *NOTE* During DAD/RS process, this code is + // executed for each timeout (probably wasted + // effort), but DAD/RS procesess are only active + // for short period of time, so it *should* not + // matter... -- msa + const TUint current = User::TickCount(); + TUint oldest_time = 0; + CIp6Route *oldest_rt = NULL; + LOG(Log::Printf(_L("\tIF %u [%S] Neighbor cache cleanup check"), iScope[0], &iName)); + for (CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + { + if (rt->IsHostRoute() && + rt->iIsRouter == 0 && // ..do not expire routers... + rt->iFlowList == NULL && // ..only, if no flows! + // ..below test should work correctly even if TickCount + // wraps around! [without the wraparound "problem", could + // just look for the smallest timestamp... -- msa] + oldest_time < (TUint)(current - rt->iTimeStamp)) + { + oldest_time = current - rt->iTimeStamp; + oldest_rt = rt; + } + } + // "long enough" = ~8*iReachableTime + if (oldest_time / 8 > iReachableTime) + // no need to test oldest_rt != NULL! if oldest_time is + // is non-zero, then also oldest_rt != NULL. -- msa + RemoveRoute(oldest_rt); + // + // Just use the *default* reachable time as a basis for + // repeating this loop [given in milliseconds, needs to + // be converted into units] + // + // *NOTE* This means that a timer is always active for + // ND interface entries... -- msa + const TUint schedule = CIp6Manager::TimerUnits(KInetNdConfig.iReachableTime, 1000); + if (next_event > schedule) + next_event = schedule; + } + +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + // RFC 5006 Changes + // Since CIP6Interface::Timeout()handles RS initiation every 30 seconds, not necessary for 5006 to do it again + // Synchronise RDNSS server list and Repository for every 30 seconds + if(iRdnssList!= NULL) + { +#ifdef _DEBUG + LOG(Log::Printf(_L("\tIF %u [%S] RDNSS Cache Cleanup"), iScope[0], &iName)); +#endif + if (iRdnssList->RdnssServerListSync(iNameSer1, iNameSer2)) + { + (void)SendNeighbors(KInet6ICMP_RouterSol, NULL, KInet6AddrNone); + LOG(Log::Printf(_L("\tIF %u [%S]RDNSS: Sending RS"), iScope[0], &iName)); + } + + // Update Nameserver repository only if iRdnssFlag was reset to zero + // Even if either iNameSer1 or iNameSer2 is elapsed iRdnssFlag was reset to zero + // Need to update both iNameSer1/iNameSer2 with KAFUnspec or a valid DNS address from iRdnssArrayList + if( (((iRdnssList->GetRdnssFlag())& RDNSS_NAMESERVER1) ==0) || + (((iRdnssList->GetRdnssFlag())& RDNSS_NAMESERVER2)== 0) + ) + { + iRdnssList->RdnssNameServerUpdate(iNameSer1,(TUint8)0); + iRdnssList->RdnssNameServerUpdate(iNameSer2,(TUint8)1); + LOG(Log::Printf(_L("\tIF RDNSS TABLE After SYNC"))); + TInt arrayCount = iRdnssList->CountRdnssEntry(); + for(TUint8 index =0;indexPrintRdnssServerList(index); + } + } + if(iRdnssList->GetRdnssFlag()) //Notify only if changed + NotifyInterfaceEvent(EventTypeModify); + } +#endif // SYMBIAN_TCPIPDHCP_UPDATE + + if (next_event < KMaxTUint) + { + LOG(Log::Printf(_L("\tIF %u [%S] Schedule next timeout after %u/%u s"), + iScope[0], &iName, next_event, TIMER_UNIT)); + Interfacer().SetTimerWithUnits(iTimeout, next_event); + } + else + { + LOG(Log::Printf(_L("\tIF %u [%S] Sleeps, no timeout"), iScope[0], &iName)); + CancelTimer(); + } + } + +CIp6Interface::~CIp6Interface() + { + // Note: Reset does some extra inits, which + // are wasted on destructor. Should not cause + // any problems... -- msa + Reset(); + LOG(Log::Printf(_L("\tIF %u [%S] Deleted"), iScope[0], &iName)); + } + +// CIp6Interface::Reset +// ******************** +/** +// Release all attached resources and set the instance into +// initial state. Can be called any time. +// +// EXCEPTION: DO NOT TOUCH 'iNetDial' in Reset! +// +// NOTE: Also used from the class destructor! +*/ +void CIp6Interface::Reset(TInt aKeepNif) + { + LOG(Log::Printf(_L("\tIF %u [%S] Reset(%d)"), iScope[0], &iName, aKeepNif)); + CancelTimer(); // Prevent any timers fromm firing + // + // Empty the hold queue + // + iHoldQueue.Free(); + // + // Remove all routes + // + CIp6Route *rh = iRouteList; + iRouteList = NULL; + iRouters = 0; // No routers left either! + while (rh != NULL) + { + CIp6Route *const r = rh; + rh = r->iNext; + // Because ALL entries are going to be deleted, there + // is no need to worry about iRouter pointers. + NotifyRouteEvent(EventTypeDelete, r); + delete r; + } + ASSERT(iFlows == 0); + // + iIsIPv6 = 0; + iIsIPv4 = 0; + iIsRouter = 0; + iIsSuspended = 0; + iIpv4Linklocal = EV4LLUnknown; + // + // Remove address information + // + TIp6AddressInfo ai = iAddress; + iAddress.iNext = NULL; + iAddress.iId = KInet6AddrNone; + iAddress.SetNoAddress(); + iAddress.iIpv4LinkLocal = 0; + for (;;) + { + if (ai.IsSet()) + NotifyAddressEvent(EventTypeDelete, ai.iId, ai.iPrefix, NULL, ai); + CIp6Address *const a = ai.iNext; + if (a == NULL) + break; + ai = a->iInfo; + delete a; + } + + // Reset the scope vector: fill all scope levels + // with unique non-zero id by loading a complement + // of the interface index into them. + for (TInt i = 1; i <= EScopeType_NET; ++i) + iScope[i] = ~iScope[0]; + + iTimeStamp.UniversalTime(); // A new "birthday" for the interface + iState = KErrNone; // is this needed?? -- msa + + // Reset some other address fields + iHwAddr.SetFamily(0); + iNameSer1.Init(0); + iNameSer2.Init(0); + + + iSMtu = 0; + iRMtu = 0; + iPMtu = 0; + + // Reset default hoplimit from the configured default + iHopLimit = Interfacer().iMaxTTL; + // + // Reset ND parameters (whether needed or not) + // + iND = KInetNdConfig; + SetReachableTime(); // Initial value + SetRetransTimer(); // Initial value + // + // Remove NIFMAN/Interface associations + // + if (aKeepNif == 0 && iNifIf) + { + // ...notify network layer, in case any hook is interested. + // Use temporary safe "nif" variable, because there could + // some callbacks from the hooks within the InterfaceDetached + // method(s). + CNifIfBase *const nif = iNifIf; + iNifIf = NULL; + if (iNifUser->iNetwork) + iNifUser->iNetwork->InterfaceDetached(iName, nif); + nif->Close(); + } +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + //RFC-5006 Changes for RDNSS option + delete iRdnssList; + iRdnssList = NULL; +#endif //SYMBIAN_TCPIPDHCP_UPDATE + } + + +TInt CIp6Interface::HaveIp4LinkLocal() + /** + * Tells whether IPv4 link-local addresses are in use. + * The possible return values are enumerated in EV4LLEnums. + */ + { + // - If the IPv4 Link-local parameter was already set for this interface, use that + // - If it was not set, apply the following search order when looking for ipv4linklocal + // 1. [interface name] - specific section in tcpip6.ini + // 2. [ip] - section in tcpip6.ini + if (iIpv4Linklocal != EV4LLUnknown) + return iIpv4Linklocal; + + TInt value = 0; + if (!Interfacer().FindVar(iName, TCPIP_INI_IPV4LINKLOCAL, value)) + iIpv4Linklocal = Interfacer().iIpv4Linklocal; + else if (value < 0 || value > 3) + iIpv4Linklocal = Interfacer().iIpv4Linklocal; + else + iIpv4Linklocal = value; + + return iIpv4Linklocal; + } + + +// +// ************************** +// CIp6Manager Implementation +// ************************** +// +// +CIp6Manager::CIp6Manager() : iTimeout(CIp6ManagerTimeoutLinkage::Timeout) + { + } + +CIfManager *CIfManager::NewL() + { + CIp6Manager *mgr = new (ELeave) CIp6Manager(); + CleanupStack::PushL(mgr); + mgr->InitL(); + CleanupStack::Pop(); + return mgr; + }; + + +TInt CIp6Manager::GetIniValue(const TDesC &aSection, const TDesC &aName, TInt aDefault, TInt aMin, TInt aMax) + /** + * Get tcpip.ini value. + * + * @param aSection The [section] name + * @param aName The variblae name + * @param aDefault The value returned, if variable is undefined, invalid or out of range. + * @param aMin The minimum allowed value + * @param aMax The maximum allowed value + * + * @return The either the aDefault, or value parsed from the ini file. + */ + { + LOG(_LIT(KFormat, "\t[%S] %S = %d")); + LOG(_LIT(KFormatInv, "\t[%S] %S = %d is invalid")); + + TInt value; + if (!FindVar(aSection, aName, value)) + value = aDefault; + else if (value < aMin || value > aMax) + { + LOG(Log::Printf(KFormatInv, &aSection, &aName, value)); + value = aDefault; + } + LOG(Log::Printf(KFormat, &aSection, &aName, value)); + return value; + } + +void CIp6Manager::InitL() + { + LOG(Log::Printf(_L("--- tcpip6 starting, version: [%S] ---"), &KInet6Version)); + + // Create EventManager instance. May return NULL. + iEventManager = MEventService::CreateEventManager(KNumClassesTcpIp6); + + { + // ini parameter value determines the destination cache granularity + // [ separate cache entry either 1=per address or 2=per prefix ] + const TInt keymode = GetIniValue(TCPIP_INI_IP, TCPIP_INI_DSTCACHE, 0, 0, 2); + if (keymode) + { + // Create Destination Cache instance. May return NULL + iDestinationCache = MDestinationCache::CreateDstCache(keymode); + if (iDestinationCache) + { + iDestinationCache->SetLifetime(GetIniValue(TCPIP_INI_IP, TCPIP_INI_DST_LIFETIME, KDstCacheLifetime, 1, KMaxTInt)); + iDestinationCache->SetMaxSize(GetIniValue(TCPIP_INI_IP, TCPIP_INI_DST_MAXSIZE, KDstCacheMaxSize, 0, KMaxTInt)); + } + } + } + // + // Create the NIF "proxies" for the protocols + // + for (TInt i = 0; i < (TInt)(sizeof(iNifUser) / sizeof(iNifUser[0])); ++i) + iNifUser[i] = new (ELeave) CIp6NifUser(*this, i == E_IPv4); + + // Just put something non-zero into iSeed + // (should use something other than 'this', as it may leak unwanted + // information out. Also, not random enough, if multiple identical + // devices are on the same net booting at same time -- fix sometime -- msa) + const TUint32 seed[2] = {(TUint32)this, ~(TUint32)this}; + iSeed = *(const TInt64*)&seed; + // + // Compute maximum interval in seconds, that can be expressed in ticks difference + // + TReal interval = (TReal)KMaxTInt * TickPeriod() / 1000000.0; + (void)Math::Int((TInt32 &)iMaxTickInterval, interval); + LOG(Log::Printf(_L("\tMaxTickInterval = %d\n"), (int)iMaxTickInterval)); + + // + // Create Timer Service + // + iTimeoutManager = TimeoutFactory::NewL(TIMER_UNIT, this, KTcpipIni_TimeoutPriority); + // + // Create a special holding route. + // Load the route with 128 prefix and no address (= illegal destination) + // => Thus, holding route is never selected by plain FindRoute! -- msa + AddRouteL(KInet6AddrNone, 128, _L("")); + // + // At this stage, the above route should be the *ONLY* one existing + // iHoldingRoute is just a copy pointer to special route entry. + // DO not use delete on it! + // + iHoldingRoute = iInterfaceList->iRouteList; + if (iHoldingRoute == NULL) + // In this case AddRouteL returns and fails to + // create first entry to the iRouteList only if + // route allocation fails due to heap. There is + // no point in going on with stack initialize, + // just leave (and and shutdown). + User::Leave(KErrNoMemory); + + ASSERT(iHoldingRoute->iNext == NULL); + iHoldingRoute->iState = CIp6Route::EHolding; + + // + // Default TTL/HopLimit + // + iMaxTTL = (TUint8)GetIniValue(TCPIP_INI_IP, TCPIP_INI_MAXTTL, KTcpipIni_Maxttl, 0, 255); + // + // Default TTL/HopLimit for link local unicast targets + // (if value is negative, the default is same as for normal unicast) + // + iLinkLocalTTL = GetIniValue(TCPIP_INI_IP, TCPIP_INI_LINKLOCALTTL, KTcpipIni_LinkLocalttl, -1, 255); + + // Set default how interface errors should affect the flows + // (If flag is 1, then interface error just moves the flow to pending + // state and waits for the same or some other interface to become + // available again..) + iNoInterfaceError = (GetIniValue(TCPIP_INI_IP, TCPIP_INI_NOIFERROR, KTcpipIni_Noiferror) != 0); + + // Set default whether a flow should be counted on the interface + // or not. When counted, positive count on interface will cause + // the NIF OpenRoute() called, and when count reaches ZERO, the + // CloseRoute() is called. + iKeepInterfaceUp = (GetIniValue(TCPIP_INI_IP, TCPIP_INI_KEEP_INTERFACE_UP, KTcpipIni_KeepInterfaceUp) != 0); + + // iMaxHoldingTime sets the approximate time limit for a flow to be waiting + // for a route or netdial completion in pending state. + // A value 0 can be used to disable expiration of the hold (= "infinite" time) + iMaxHoldingTime = (TUint)GetIniValue(TCPIP_INI_IP, TCPIP_INI_MAXHOLDTIME, KTcpipIni_Maxholdtime, 0, iMaxTickInterval); + + // iShutdownDelay defines the time to wait before daemons are killed + // after last user leaves the stack. + iShutdownDelay = GetIniValue(TCPIP_INI_IP, TCPIP_INI_SHUTDOWN_DELAY, KTcpipIni_ShutdownDelay, 0, KMaxTInt); + + // iIpv4Linklocal enables automatic IPv4 link local addresses from + // 169.254.0.0/16 + iIpv4Linklocal = GetIniValue(TCPIP_INI_IP, TCPIP_INI_IPV4LINKLOCAL, KTcpipIni_Ipv4Linklocal, 0, 3); + + // Allow disabling a part of "DIID" (if set, do not defend my + // interface ID's aggressively. + iNoDefendId = (GetIniValue(TCPIP_INI_IP, TCPIP_INI_NODEFENDID, KTcpipIni_NoDefendId) != 0); + + // Control ND probing. If enabled, stack probes for the destination + // address on all interfaces in the scope, when destination does not + // have a route (e.g. if system has no default route). This setting + // is ignored if disabled if the source address is an IPv4 link local + // or else the link local would not function except for neighbours + // with routes already discovered and cached. This is necessary for + // compliance with the ZEROCONF RFC. + iProbeAddress = (GetIniValue(TCPIP_INI_IP, TCPIP_INI_PROBEADDRESS, KTcpipIni_ProbeAddress) != 0); + + // Control support of the Route Information option. The option is enabled, if the value + // is non-zero. The value defines the option type to be used. + iRA_OptRoute = (TUint8)GetIniValue(TCPIP_INI_IP, TCPIP_INI_RA_OPT_ROUTE, 0, 0, 255); + +#ifndef SYMBIAN_TCPIPDHCP_UPDATE + // Control support of the RDNSS option. The option is enabled, if the value + // is non-zero. The value defines the option type to be used. + iRA_OptDns = (TUint8)GetIniValue(TCPIP_INI_IP, TCPIP_INI_RA_OPT_RDNSS, 0, 0, 255); +#endif //SYMBIAN_TCPIPDHCP_UPDATE + } + +TBool CIp6Manager::LoadConfigurationFile() + { + if (iConfig) + return TRUE; // Already loaded! + // if iConfigErr != 0 (KErrNone), then an attempt for + // loading configuration has been made and failed, + // assume it never will succeed and avoid further + // attemps on each FindVar... + if (iConfigErr) + return FALSE; + LOG(Log::Printf(_L("CIp6Manager::LoadConfigurationFile(): %S"), &TCPIP_INI_DATA)); + TRAP(iConfigErr, iConfig = CESockIniData::NewL(TCPIP_INI_DATA)); + return (iConfig != NULL); + } + +// +// Access to the configuration file (tcpip.ini) +// +TBool CIp6Manager::FindVar(const TDesC &aSection, const TDesC &aVarName, TPtrC &aResult) + { + if (LoadConfigurationFile()) + { + ASSERT(iConfig); // <-- lint gag + return iConfig->FindVar(aSection, aVarName, aResult); + } + return FALSE; + } + +TBool CIp6Manager::FindVar(const TDesC &aSection, const TDesC &aVarName, TInt &aResult) + { + if (LoadConfigurationFile()) + { + ASSERT(iConfig); // <-- lint gag + return iConfig->FindVar(aSection, aVarName, aResult); + } + return FALSE; + } + +CIp6Manager::~CIp6Manager() + { + LOG(Log::Printf(_L("~CIp6Manager()"))); + + CancelTimer(); + + StopDaemons(); + + // Release all interfaces + + while (iInterfaceList) + { + CIp6Interface *iface = iInterfaceList; + iInterfaceList = iface->iNext; + delete iface; + } + delete iTimeoutManager; + delete iConfig; + // + // Destroy NIF "proxies" + // + for (TInt i = 0; i < (TInt)(sizeof(iNifUser) / sizeof(iNifUser[0])); ++i) + delete iNifUser[i]; + + ASSERT(iFlows == 0); + + delete iEventManager; + delete iDestinationCache; + LOG(Log::Printf(_L("--- tcpip6 finished, version: [%S] ---"), &KInet6Version)); + } + +// ************************* +// CIp6Manager::StartDaemons +// ************************* +void CIp6Manager::StartDaemons() + { + ASSERT(iDaemons == NULL); + LOG(Log::Printf(_L("CIp6Manager::StartDaemons()"))); + + // + // Create and start Daemons (as specified in TCPIP.INI) + // + TPtrC daemons; + if (FindVar(TCPIP_INI_START, TCPIP_INI_DAEMONS, daemons)) + { + TLex start(daemons); + while (!start.Eos()) + { + start.Mark(); + while (!start.Eos() && start.Peek() != ',') + start.Inc(); + + TPtrC demon = start.MarkedToken(); + if (!start.Eos()) + start.Inc(); // Skip ',' + TPtrC name; + if (FindVar(demon, TCPIP_INI_FILENAME, name)) + { + CIp6Daemon *d = new CIp6Daemon; + if (!d) + return; // should probably tell someone about this fail! + d->iNext = iDaemons; + iDaemons = d; + d->Start(demon, name); + } + else + { + // Should probably report this. It's an TCPIP.INI error + // to reference a daemon section that does not exist! + LOG(Log::Printf(_L("CIp6Manager::InitL() '[%S] filename' not found\n"), &demon)); + } + } + } + } + +void CIp6Manager::StopDaemons() + { + LOG(Log::Printf(_L("CIp6Manager::StopDaemons()"))); + // Kill active daemons + // + while (iDaemons) + { + CIp6Daemon *const d = iDaemons; + iDaemons = iDaemons->iNext; + delete d; + } + } + +// CIp6Manager::Timeout +// ******************** +/** +// The manager Timeout is currently used only for "sluggish +// shutdown", so implentation is very simple... +*/ +void CIp6Manager::Timeout(const TTime & /*aStamp*/) + { + LOG(Log::Printf(_L("CIp6Manager::Timeout() iUsers=%d"), (int)iUsers)); + if (iUsers == 0) + StopDaemons(); + } + + +// CIp6Manager::TimerUnits +// *********************** +// +TUint CIp6Manager::TimerUnits(const TUint aDelay, const TUint aUnit) + /** + * Convert a delay time to internal timer units. + * + * If the converted result would exceed KMaxTUint, then + * KMaxTUint is returned. + * + * @param aDelay The timer delay in specified units + * @param aUnit The unit definition (fraction of second, [1..1000000]) + * @return The delay in internal units [0..KMaxTUint]. + */ + { + if (aUnit == TIMER_UNIT) + return aDelay; + +#ifdef MAKE_TINT64 + const TInt64 delay = (MAKE_TINT64(0U, TIMER_UNIT) * MAKE_TINT64(0U, aDelay) + MAKE_TINT64(0U, aUnit - 1)) / MAKE_TINT64(0U, aUnit); + return I64HIGH(delay) ? KMaxTUint : I64LOW(delay); +#else + const TInt64 delay((TInt64(0U, TIMER_UNIT) * TInt64(0U, aDelay) + TInt64(0U, aUnit - 1)) / TInt64(0U, aUnit)); + return delay.High() ? KMaxTUint : delay.Low(); +#endif + } + + +// CIp6Manager::SetTimerSeconds +// **************************** +void CIp6Manager::SetTimer(RTimeout &aHandle, TUint32 aDelay) + /** + * Set the timeout event on a handle. + * + * @param aHandle The timeout handle + * @param aDelay The timer delay in seconds + */ + { +#if TIMER_UNIT == 1 + // Timer unit is 1s, aDelay can be used as is. + iTimeoutManager->Set(aHandle, aDelay); +#else + // Timer unit not 1s, aDelay must be adjusted. + static const TUint KMaxDelay = KMaxTUint / TIMER_UNIT; + iTimeoutManager->Set(aHandle, aDelay > KMaxDelay ? KMaxTUint : aDelay * TIMER_UNIT); +#endif + } + +// CIp6Manager::IncUsers +// ********************* +// Increment users +// +void CIp6Manager::IncUsers() + { + iUsers++; + LOG(Log::Printf(_L("\t\tIncUsers: Users=%d Nifs=%d"), iUsers, iNifCount)); + if (iDaemons == NULL) + StartDaemons(); + } + +// CIp6Manager::DecUsers +// ********************* +// Decrement users count +// +void CIp6Manager::DecUsers() + { + __ASSERT_ALWAYS(iUsers > 0, User::Panic(_L("iUsers"), iUsers)); + if (--iUsers == iNifCount) + { + // Only interfaces remain, no real clients + Nif::NetworkLayerClosed(*iNifUser[E_IPv6]); + Nif::NetworkLayerClosed(*iNifUser[E_IPv4]); + } + LOG(Log::Printf(_L("\t\tDecUsers: Users=%d Nifs=%d"), iUsers, iNifCount)); + if (iUsers == 0) + { + //StopDaemons(); + // + // Sometimes aggressive shutdown causes problems in system + // components. Use a small delay before really activating + // the daemon killer... + SetTimer(iShutdownDelay); + } + } + +// CIp6Manager::PacketAccepted +// *************************** +// +TInt CIp6Manager::PacketAccepted(const TUint32 aIndex) + { + const CIp6Interface *const iface = FindInterface(aIndex); + if (iface) + { + if (iface->iNifIf && iface->iNifIf->Notify()) + return iface->iNifIf->Notify()->PacketActivity(EIncoming, 0, TRUE); + return KErrNone; + } + return KErrNotFound; + } + +// +// CIp6Manager::NewFlow +// ******************** +// Create a new flow instance +// +CFlowContext *CIp6Manager::NewFlowL(const void *aOwner, MFlowManager *aManager, TUint aProtocol) + { + CFlowContext *flow = new (ELeave) CIp6Flow(aOwner, aManager, *this, aProtocol); + return flow; + } + +CFlowContext *CIp6Manager::NewFlowL(const void *aOwner, MFlowManager *aManager, CFlowContext &aFlow) + { + return new (ELeave) CIp6Flow(aOwner, aManager, *this, aFlow); + } + + +// CIp6Manager::Register +// ********************* +// Attach a protocol to NIF "proxy" +MNifIfUser *CIp6Manager::Register(MNetworkServiceExtension *aNetwork) + { + TServerProtocolDesc info; + CIp6NifUser *ifuser = NULL; + + aNetwork->Protocol()->Identify(&info); + if (info.iAddrFamily == KAfInet6) + { + (ifuser = iNifUser[E_IPv6])->iNetwork = aNetwork; + } + else if (info.iAddrFamily == KAfInet) + { + (ifuser = iNifUser[E_IPv4])->iNetwork = aNetwork; + } + return ifuser; + } + +// +// CIp6Manager::Unregister +// *********************** +// Remove all references to the specified protocol +void CIp6Manager::Unregister(MNetworkServiceExtension *aNetwork) + { + for (TInt i = 0; i < (TInt)(sizeof(iNifUser) / sizeof(iNifUser[0])); ++i) + if (iNifUser[i]->iNetwork == aNetwork) + iNifUser[i]->iNetwork = NULL; + } + +// CIp6Manager::IcmpSend +// ********************* +/** +// Wrap a packet into ICMP error reply and send it out. +// +// Delegate the task to the network instance MNetworkServiceExtension::IcmpWrap method. +// +// @param aPacket +// The RMBuf chain containing the IP packet in packet state +// @param aIcmp +// The 32 bit value containing type and code for both IPv4 and IPv6. The type and +// code to be used are chosen based on the actual IP version of the packet. +// @param aParameter +// The 32 bit value to be placed as the "parameter" field of the ICMP header. +// @param aMC +// A flag, when non-Zero, forces sending of ICMP, even if the packet destination +// was a multicast address (see MNetworkService::Icmp4Send and +// MNetworkService::Icmp6Send). +*/ +void CIp6Manager::IcmpSend(RMBufChain &aPacket, const TIcmpTypeCode aIcmp, const TUint32 aParameter, const TInt aMC) + { + // Just use any "network" instance, and assume it works just + // as well whether IPv4 or IPv6... + MNetworkServiceExtension *const network = iNifUser[E_IPv6]->iNetwork ? iNifUser[E_IPv6]->iNetwork : iNifUser[E_IPv4]->iNetwork; + if (network) + network->IcmpWrap(aPacket, aIcmp, aParameter, aMC); + // + // Release packet (if not passed on) + // + aPacket.Free(); + return; + } + +// +// CIp6Manager::IcmpError +// ********************** +/** +// Gets a peek at all received ICMP error messages before they +// are passed to the upper layers. This is called from the +// IcmpError() method of the IP layer. +// +// @return +// @li < 0, if packet has been released (packet will not +// go to the upper layer after this), +// @li = 0, the usual return, packet looked and it can be +// passed to the upper layers +// @li > 0, *NOT USED NOW*, Treat as = 0 as default +*/ +TInt CIp6Manager::IcmpError(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo) + { + // note: iParameter is fixed by ICMP protocol as 32 bit entity. + // Assume that "natural" TUint will always be at least 32 bits + // (compiler should warn, if this is not true). + TUint mtu = aInfo.iParameter; + + // + // Currently the only interesting thing here is to detect the + // MTU affecting ICMP's for both IPv6 and IPv4. + // + if (aInfo.iIcmp == KProtocolInet6Icmp) + { + // IPv6 ICMP Errors + // + // The only insteresting error is KInet6ICMP_PacketTooBig, + // + if (aInfo.iType != KInet6ICMP_PacketTooBig) + return 0; + if (mtu < STATIC_CAST(TUint,KInet6MinMtu)) + return 0; // Cannot set it smaller than minimum MTU! + } + else if (aInfo.iIcmp == KProtocolInetIcmp) + { + // IPv4 ICMP Errors + // + if (aInfo.iType == KInet4ICMP_Redirect) + { + CIp6Interface *const ifp = FindInterface(aInfo.iInterfaceIndex); + if (ifp) + ifp->Ip4RedirectHandler(aPacket, aInfo); + return 0; + } + // The only interesting thing is the Unreachable/Fragmentation needed + // (RFC-792 + RFC-1192) + if (aInfo.iType != KInet4ICMP_Unreachable || aInfo.iCode != 4) + return 0; + // + // Check the parameter as per RFC-1192 (additions to RFC-972) + if (mtu == 0) + mtu = 576; // ICMP does not include next hop MTU, use 576 + else + { + mtu &= 0xffff; // Mask off the "unused high order bits" + if (mtu < STATIC_CAST(TUint,KInetMinMtu)) + return 0; // Cannot be smaller than 68! + } + } + else + return 0; + // + // A new minimum mtu value has been received... + // + // + // We assume the info still holds the original data as set in the + // interface. + // + const CIp6Interface *const iface = FindInterface(aInfo.iInterfaceIndex); + if (iface == NULL) + return 0; // Can't locate interface (should not happen + // unless interface actually died after this + // packet got in). + // + // Poor man's Path MTU algorithm. Just maintain single Path MTU + // for a interface. [first obvious optimization would be to use + // interface MTU at least for the local addresses]. Enhance as + // needed later.. -- msa + // + if (mtu >= (TUint)iface->iPMtu) + return 0; // Too Big error MUST NOT increase existing Path MTU! + + // + // Path MTU has been decreased, notify all affected flows! + // + iface->NotifyFlowsPmtu(mtu); + + // Store path mtu value to destination cache + if (iDestinationCache) + { + TInetAddr dst = TInetAddr::Cast(aInfo.iDstAddr); + const TCacheInfo *ci = iDestinationCache->Find(dst); + + // valid cached PMTU must not be increased + if (!ci || ci->iMetrics[TCacheInfo::EPathMTU] > mtu) + { + // Nothing useful to be done here with error code. + TRAP_IGNORE(iDestinationCache->SetL(dst, TCacheInfo::EPathMTU, mtu)); + } + } + + return 0; + } + + +// CIp6Manager::MoveToHolding +// ************************** +// +void CIp6Manager::MoveToHolding(CIp6Flow &aFlow) const + { + aFlow.iChanged = 1; // When put on hold, always require a reconnect + if (iHoldingRoute == NULL) + return; // Should never happen... + if (aFlow.iStatus >= 0) // ..don't overwrite error states + aFlow.iStatus = EFlow_PENDING; + + if (aFlow.iRoute != iHoldingRoute) + { + // This is ugly: a flow can be in holding, but at arrival + // of router advertisement with default route, it will + // attach to the interface (and off holding). However, if + // source address is not available, it gets punted back + // to holding, with unfortunate side effect of resetting + // the time stamp and preventing expire. A somewhat ugly + // quick solution: time stamp is set only when it's zero, + // and RefreshFlow is changed to zero the stamp when it + // gets past source address selection... -- msa + if (aFlow.iTimeStamp == 0) + // NOTE: on rare accounts, TickCount can also be 0, + // but, it should not break things too much... + aFlow.iTimeStamp = User::TickCount(); + iHoldingRoute->Attach(aFlow); + if (!iHoldingRoute->IsTimerActive()) + iHoldingRoute->Timeout(); + } + } + +void CIp6Manager::MoveToHolding(CIp6Route &aRoute) const + { + if (iHoldingRoute != &aRoute) + { + while (aRoute.iFlowList != NULL) + MoveToHolding(*aRoute.iFlowList); + } + } + +// CIp6Manager::ScanHoldings +// ************************* +// +void CIp6Manager::ScanHoldings() + { + iScanHolding = 0; + if (!iHoldingRoute) + return; + TFlowNotifyList list; + for (CIp6Flow *f = iHoldingRoute->iFlowList; f != NULL; f = f->iNext) + list.Insert(*f); + list.Deliver(EFlow_READY); + } + +// +// CIp6Manager::GetInterfaceByNameL +// ******************************** +/** +// Get interface by name (and create a new entry, if not found) +// +// @returns non-NULL always or leaves +*/ +CIp6Interface *CIp6Manager::GetInterfaceByNameL(const TDesC &aName) + { + // + // Look if the interface already exists + // + CIp6Interface **h, *iface; + + for (h = &iInterfaceList;; h= &iface->iNext) + { + if ((iface = *h) == NULL) + { + // + // A new interface, create an instance + // + // *NOTE* + // 1) assume iInterfaceIndex is an ever increasing sequence + // (some worry about wrap around... -- msa) + // 2) new interfaces are always added to the *END* of + // the list + // => The interfaces on the list are *ALWAYS* in increasing + // order by their iIndex fields! + // ** The above FACT is relied on in other methods, such + // as InterfaceInfo! + // + iface = new (ELeave) CIp6Interface(*this, ++iInterfaceIndex, aName); + iface->iNext = NULL; + iface->iNifUser = iNifUser[0]; // Doesn't matter whether IPv4 or IPv6 (it will be changed as needed). + ASSERT(iNifUser[0] != NULL); + *h = iface; + iface->Reset(); // ..some members have non-ZERO defauls, so Reset is also called! + break; + } + else if (aName.Compare(iface->iName) == 0) + break; // Interface with specified name already exists + } + return iface; + } + +// CIp6Manager::RemoveInterface +// **************************** +// Brutally tear the specified interface down and release all associated resources +void CIp6Manager::RemoveInterface(CIp6Interface *aIf) + { + CIp6Interface **h, *iface; + + for (h = &iInterfaceList; (iface = *h) != NULL; h= &iface->iNext) + if (iface == aIf) + { + // Remove from the list and destroy! + *h = iface->iNext; + delete iface; + return; + } + // Should NEVER get here! + ASSERT(0); + } + +// CIp6Manager::FindRoute +// ********************** +/** +// Locate route entry who has the longest matching +// prefix with destination. (A default route can be +// expressed with zero length prefix, which will match +// any address) +// +// Only interfaces with matching ScopeId are searched. +*/ +CIp6Route *CIp6Manager::FindRoute + (const TIp6Addr &aDst, const TUint32 aDstId, const TUint aDstType, + const TIp6Addr &aSrc, const TUint32 aSrcId) const + { + // + // Search Over all interfaces "within scope" + // + CIp6Route *route = NULL; + + if (aDstType > EScopeType_NET) + return NULL; + + if (!TIp46Addr::Cast(aSrc).IsUnspecified()) + { + // Find route by source and destination (need to do the hard way in + // case we allow the same source address with multiple interfaces). + // [easy way would be: find interface with the address and just check + // route on it -- msa] + // + const TUint srcType = (TUint)(aSrc.Scope() - 1); + if (srcType > EScopeType_NET) + return NULL; + + for (const CIp6Interface *ifp = iInterfaceList; ifp != NULL; ifp = ifp->iNext) + { + if ((aSrcId == 0 || ifp->iScope[srcType] == 0 || ifp->iScope[srcType] == aSrcId) && + (aDstId == 0 || ifp->iScope[aDstType] == 0 || ifp->iScope[aDstType] == aDstId) && + ifp->IsMyAddress(aSrc)) + route = ifp->FindRoute(aDst, route); + } + } + else if (aSrcId != 0) + { + // When a source address is not specified, but source id is given, then it is + // assumed that the source ID is the interface index and will limit the route + // search to a specific interface. + for (const CIp6Interface *ifp = iInterfaceList; ifp != NULL; ifp = ifp->iNext) + if (ifp->iScope[0] == aSrcId) + { + route = ifp->FindRoute(aDst, route); + break; + } + } + else + { + // Find route by destination alone + for (const CIp6Interface *ifp = iInterfaceList; ifp != NULL; ifp = ifp->iNext) + { + if ((aDstId == 0 || ifp->iScope[aDstType] == 0 || ifp->iScope[aDstType] == aDstId)) + route = ifp->FindRoute(aDst, route); + } + } + return route; + } + + +// CIp6Manager::ProbeDestination +// ***************************** +void CIp6Manager::ProbeDestination + (const TIp6Addr &aDst, const TUint32 aDstId, const TUint aDstType, + const TIp6Addr &aSrc, const TUint32 aSrcId) const + { + // No probing if destination is not fully defined + + if (!aDstId || aDstType > EScopeType_NET) + return; + + if (!TIp46Addr::Cast(aSrc).IsUnspecified()) + { + // The source address limits the valid interfaces for probing. + // + const TUint srcType = (TUint)(aSrc.Scope() - 1); + if (srcType > EScopeType_NET) + return; + + for (CIp6Interface *ifp = iInterfaceList; ifp != NULL; ifp = ifp->iNext) + { + if (ifp->NeedsND() && + ifp->iScope[srcType] == aSrcId && + ifp->iScope[aDstType] == aDstId + ) + { + TIp6AddressInfo *info = ifp->IsMyAddress(aSrc); + + // We override the probe address setting for link local source addresses - + // they are not routable and therefore useless otherwise. + if( info && ( iProbeAddress || info->iIpv4LinkLocal ) ) + { + (void)ifp->StartProbeND(aSrc, aDst); + } + } + } + } + else if (aSrcId != 0) + { + // When a source address is not specified, but source id is given, then it is + // assumed that the source ID is the interface index and will limit the + // search to a specific interface. + CIp6Interface *const ifp = FindInterface(aSrcId); + TIp6Addr src; + if (ifp && ifp->NeedsND()) + { + if( ifp->SelectSource(src, aDst) ) + { + if( iProbeAddress ) + { + (void)ifp->StartProbeND(src, aDst); + } + else + { + TIp6AddressInfo *info = ifp->IsMyId( src ); // do not need to search anycast addresses + + // We override the probe address setting for link local source addresses - + // they are not routable and therefore useless otherwise. + if( info && info->iIpv4LinkLocal ) + { + (void)ifp->StartProbeND(src, aDst); + } + } + } + else + { + const TIp6AddressInfo* address = ifp->FindIpv4LinkLocalAddr(); + + // If no appropriate routable source address exists, start a probe if the interface + // has an IPv4 link local address. + if( address && address->IsAssigned() ) + { + (void)ifp->StartProbeND(address->iId, aDst); + } + } + } + } + else + { + TBool probeStarted = EFalse; + + // Select by destination only. If no source address can be found, start a probe + for (CIp6Interface *ifp = iInterfaceList; ifp != NULL; ifp = ifp->iNext) + { + if (ifp->NeedsND() && ifp->iScope[aDstType] == aDstId) + { + TIp6Addr src; + if (ifp->SelectSource(src, aDst)) + { + if( iProbeAddress ) + { + (void)ifp->StartProbeND(src, aDst); + + probeStarted = ETrue; + } + else + { + TIp6AddressInfo *info = ifp->IsMyId( src ); // do not need to search anycast addresses + + // We override the probe address setting for link local source addresses - + // they are not routable and therefore useless otherwise. + if( info && info->iIpv4LinkLocal ) + { + (void)ifp->StartProbeND(src, aDst); + + probeStarted = ETrue; + } + } + } + } + } + + // If no interface has an appropriate routable source address, start a probe + // on each interface with an IPv4 link local address. + if( !probeStarted ) + { + for (CIp6Interface *ifp = iInterfaceList; ifp != NULL; ifp = ifp->iNext) + { + if (ifp->NeedsND() && ifp->iScope[aDstType] == aDstId) + { + const TIp6AddressInfo* address = ifp->FindIpv4LinkLocalAddr(); + + if( address && address->IsAssigned() ) + { + (void)ifp->StartProbeND(address->iId, aDst); + } + } + } + } + } + } + + +TInt CIp6Manager::GetDstCachePathMtu(const TIp6Addr& aDstAddress, TUint32 aScopeId) const +/** +Returns Path MTU value stored in destination cache with given IP address. + +@param aDstAddress IP destination address to be searched from the destination cache. +@param aScopeId Scope ID of the destination. RefreshFlow() has selected this. + +@return Path MTU if value was stored in destination cache. If destination cache is not available +or given address could not be found, 0 is returned. +*/ + { + if (!iDestinationCache) + { + return 0; + } + + TInetAddr dst(aDstAddress, 0); + dst.SetScope(aScopeId); + const TCacheInfo *cinfo = iDestinationCache->Find(dst); + LOG(TLogAddressPrefix logdst(dst)); // For logging, the destination address as a string. + if (!cinfo) + { + LOG(Log::Printf(_L("\t\tDstCache Path MTU for %S -- No match"), &logdst)); + return 0; + } + + LOG(Log::Printf(_L("\t\tDstCache Path MTU for %S -- Found MTU = %d"), + &logdst, cinfo->iMetrics[TCacheInfo::EPathMTU])); + + return cinfo->iMetrics[TCacheInfo::EPathMTU]; + } + + +void *CIp6Manager::GetApiL(const TDesC8& aApiName, TUint* aVersion) + { + if (aApiName == _L8("MEventService")) + { + if (!iEventManager) + { + User::Leave(KErrInetUnsupportedApi); + } + return EXPORT_API_L(MEventService, iEventManager, aVersion); + } + + if (aApiName == _L8("MNetworkInfo")) + return EXPORT_API_L(MNetworkInfo, this, aVersion); + + if (aApiName == _L8("MDestinationCache")) + { + // It is possible that the destination cache has not been instantiated + // (e.g. due to ini param), or its initialization has failed. + // The leave error below is not the most ideal, perhaps + if (!iDestinationCache) + { + User::Leave(KErrInetUnsupportedApi); + } + return EXPORT_API_L(MDestinationCache, iDestinationCache, aVersion); + } + + User::Leave(KErrInetUnsupportedApi); + // NOTREACHED + return NULL; + } + + +CIp6Route *CIp6Interface::FindNeighbor(const TIp6Addr &aDst) const + { + // Only a neighbor cache entry is accepted! + for (CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + if (rt->IsHostRoute() && aDst.IsEqual(rt->iPrefix)) + return rt; + return NULL; + } + + +// CIp6Interface::FindRoute +// ************************ +CIp6Route *CIp6Interface::FindRoute(const TIp6Addr &aDst, CIp6Route *aRoute) const + /** + * Locate the best route from this interface matching the destination. + * + * @param aDst The destination address + * @param aRoute The current best route, or NULL if none found yet. + * + * @return + * @li if no better route is found, the return value is aRoute. + * @li if a better route is found, return value is the new better route. + */ + { + // The way sending packets is now implemented, requires that if the destionation + // is my own address, then the route assigned to the flow must indicate that + // (must be ELoopback). [This way, the destination doesn't need to be checked + // for every packet--a simple test for route type is enough]. + // + // Address is my address, if the ID (which is not a proxy) matches my id AND, + // if the prefix part fully matches one of the ELoopback routes. + CIp6Route *rt = NULL; // silly GCC want's this to be silent... + + const TIp6AddressInfo *const id = IsMyId(aDst); + if (id && !id->IsProxy() && (rt = IsMyPrefix(aDst, *id)) != NULL) + { + // ..could test if id IsAssigned(), but only "wrong" + // thing that happens is, that a loopback route is returned + // for the flow and no packets with tentative address will + // go out! (and as "tentative" is very transient state, + // this should not happen easily) + // + // If a destination is my address on this interface, + // don't even consider any other routes. + // + return rt; + } + // + // The route with best weight is selected. The weight value is + // computed as follows + // -2, if no route yet + // -1, if route is not "reachable" + // prefix length, if route is "reachable" + // + // "reachable" an internal concept to this function and is + // defined as: route is reachable if it is not a gateway route, + // or if it is a gateway route and a host route to the gateway + // exist with ISROUTE set! + // + // *NOTE* The gateway test intentionally excludes the ERedirect + // gateways! + + + TInt weight; + // If destination is IPv4 address, the route must + // match at least 96 bits to be acceptable! + const TInt min_match = aDst.IsV4Mapped() ? 96 : 0; + + if (aRoute == NULL) + weight = -2; + else + weight = (aRoute->iState != CIp6Route::EGateway || aRoute->iRouter) ? aRoute->iLength : -1; + + for (rt = iRouteList; rt != NULL; rt = rt->iNext) + { + if (rt->IsMyPrefix()) + continue; // Ignore "my prefix" entries. Looking for "true" routes only + if (rt->iIsProbing) + continue; // Ignore "probing only" routes (ND host route). + if (min_match > rt->iLength) + continue; // Ignore too short prefixes (IPv4 needs at least 96). + + const TInt w = (rt->iState != CIp6Route::EGateway || rt->iRouter) ? rt->iLength : -1; + if (weight > w) + continue; // Already better route exists, no need to compare address! + if (rt->iPrefix.Match(aDst) < rt->iLength) + continue; // Does not match (full prefix must match!) + // + // *NOTE* if aRoute == NULL, then weight == -2 and thus cannot be equal + // to w (always > -2) => No need to test if aRoute is NULL!! + // Metric only affects "reachable" routes (if it affected non-"reachable", + // system would never try an alternate gateway route with larger metric, + // even if the better metric route would fail consistently!) -- msa +//lint -e{613} + if (weight == w && w >= 0 && rt->iMetric > aRoute->iMetric) + continue; // Weights are equal, previous route has better metric. + + // *WARNING* The above test causes the behaviour that the last matching + // route is returned, when none of the routers appear reachable. This + // is utilized by "SelectNexthop" to implement the Round-Robin attempts + // to contact routers, when none is reachable... + + aRoute = rt; + weight = w; + } + return aRoute; + } + +// CIp6Interface::GetDefGateway +// ************************ +// Get the default gateway address for this interface matching the gateway address type +// +void CIp6Interface::GetDefGateway(const TBool aIsIPv4, TInetAddr &aAddr) const + { + static const TIp6Addr prefix = {{{0,0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,0,0}}}; + TUint length = aIsIPv4 ? 96 : 0; + + CIp6Route *located_rt = NULL; + for (CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + { + if (!rt->IsGateway()) + continue; + + if (length != rt->iLength) + continue; // Ignore non-default routes + + if (!aIsIPv4 && (rt->iPrefix.Match(prefix) < length)) + continue; // Does not match (full prefix must match!) + + if (located_rt && (rt->iMetric > located_rt->iMetric)) + continue; // previous route has better metric + + located_rt = rt; + } + + if (located_rt) + located_rt->iAddress.GetAddress(aAddr); + else + aAddr.SetAddress(KInet6AddrNone); + + } + +// CIp6Interface::RouterChanged +// **************************** +void CIp6Interface::RouterChanged(CIp6Route *const aRouter) + /** + * Maintain iRouter pointers. + * + * A host route entry has been changed to a router or ceased + * to be a router. Update all gateway routes that would use + * this router by patching the iRouter entry + * + * When called, the iIsRouter value must be already set to + * the new value! + */ + { + ASSERT(aRouter->IsHostRoute()); + if (!aRouter->IsHostRoute()) + return; // Should never get here (must only be called with host routes) + + if (aRouter->iIsRouter) + { + iRouters++; + // Add/Overwrite pointer to the matching gateway entries + for (CIp6Route *rt = iRouteList; rt != NULL; rt = rt->iNext) + { + // There should be no pointers to this route before this operation! + ASSERT(rt->iRouter != aRouter); + if (rt->IsGateway() && // Only Gateway routes are affected! + rt->iAddress.Ip6Address().IsEqual(aRouter->iPrefix)) + rt->iRouter = aRouter; + } + // Should check flows in iHoldingRoute, if any could use this router now? + } + else + { + iRouters--; + // Remove all references to the this router (if all is working as intended + // they should all be gateway entries and address should match, but to be + // safe, just remove all without testing (in NON-debug release) + // + CIp6Route **h, *rt; + for (h = &iRouteList; (rt = *h) != NULL; ) + { + if (rt->iRouter == aRouter) + { + ASSERT(rt->IsGateway() && rt->iAddress.Ip6Address().IsEqual(aRouter->iPrefix)); + rt->iRouter = NULL; + // Punt all flows, if any, to holding route! + // (this action may need to be considered yet). + Interfacer().MoveToHolding(*rt); + if (rt->iState == CIp6Route::ERedirect) + { + // If a "redirect" route loses it's target router, + // redirect is cancelled -- remove the redirect + // route from the route list. + *h = rt->iNext; + delete rt; + continue; + } + } + h = &rt->iNext; + } + } + } + +// +// CIp6Interface::SelectNextHop +// **************************** +// Returns NULL, if no usable next hop found! +CIp6Route *CIp6Interface::SelectNextHop(const TIp6Addr &aDst, const TIp6Addr &aSrc, CIp6Route *aRoute) + { + ASSERT(aRoute != NULL); +#ifdef _LOG + TLogAddressPrefix logtmp(aDst); + TLogAddressPrefix logprf(aRoute->iPrefix, aRoute->iLength); +#endif + + if (aRoute->IsGateway()) + { +#ifdef _LOG + TLogAddressPrefix logsrc(aRoute->iAddress.Ip6Address()); +// Log::Printf(_L("\tNEXTHOP [%S] for [%S] ROUTE %d [%S] to GATEWAY [%S]"), +// &iName, &logtmp, aRoute->iIndex, &logprf, &logsrc); + Log::Printf(_L("\tIF %u [%S] NEXTHOP for [%S] is ROUTE %d [%S] to GATEWAY [%S]"), + iScope[0], &iName, &logtmp, aRoute->iIndex, &logprf, &logsrc); +#endif + if (aRoute->iRouter == NULL) + { + // None of possible routers have host entries. + + // Move the selected route to the front of the + // iRouteList to implement the "Round-Robin" + // requirement of default router selection + // THIS IS SOMEWHAT TRICKY! It depends on the + // behaviour of FindRoute() method to return the + // *LAST* matching route entry in case where no + // routers are reachable. -- msa + MoveToFront(aRoute); + // Locate the neighbour cache entry for the gate (or create one if not exist yet). Do not + // set or clear ISROUTER for IPv6 gateway! We don't know whether it is a router or not! + // With IPv4, there ND (=ARP) has no ISROUTER feature, so we just have to assume all + // IPv4 hosts in gateway routes are implicitly routers. + const TUint flags = aRoute->iAddress.Ip6Address().IsV4Mapped() ? KRouteAdd_ISROUTER : 0; + CIp6Route *const n = GetRoute(aRoute->iAddress.Ip6Address(), 128, flags); + if (n && n->iState == CIp6Route::EIncomplete) + { + // + // Nothing is known about the neighbor yet, start ND + // + LOG(Log::Printf(_L("\tIF %u [%S] NEXTHOP ROUTER (ND ROUTE %u) needed for [%S]"), iScope[0], &iName, n->iIndex, &logsrc)); + n->StartND(aSrc); + } + // If in above the neighbor cache already existed in some other state thatn EIncomplete, + // it means that the ISROUTER is not set for this host, and it cannot be used as a router + // currently. => Cannot use this gateway! + // + // Cannot assign the next hop to the gateway, because it is not yet known if + // it is actually a router! Keep in holding! + return NULL; + } + return aRoute; + } + if(!aRoute->IsOnlink()) + { +#ifdef _LOG + // assume route prefix is in logtmp + if (aRoute->IsMyPrefix()) + Log::Printf(_L("\tIF %u [%S] NEXTHOP for [%S] is LOOPBACK ROUTE %u [%S]"), iScope[0], &iName, &logtmp, aRoute->iIndex, &logprf); + else if (aRoute->iIsMulticast) + Log::Printf(_L("\tIF %u [%S] NEXTHOP for [%S] is MULTICAST ROUTE %u [%S]"), iScope[0], &iName, &logtmp, aRoute->iIndex, &logprf); + else if (aRoute->IsHostRoute()) + Log::Printf(_L("\tIF %u [%S] NEXTHOP for [%S] is HOST ROUTE %u [%S]"), iScope[0], &iName, &logtmp, aRoute->iIndex, &logprf); + else + Log::Printf(_L("\tIF %u [%S] NEXTHOP for [%S] is NOT ONLINK ROUTE %u [%S]"), iScope[0], &iName, &logtmp, aRoute->iIndex, &logprf); +#endif + return aRoute; + } + // This route is for onlink determination. Finding this + // means that the destination/next hop is on link, but + // no Host route for it exists yet. Create the host route. + // + // Note: LinkLocal destination is not handled in any special + // way. Interface supporting link local (fe80::/10) destinations + // must define the ONLINK route for it! + LOG(Log::Printf(_L("\tIF %u [%S] NEXTHOP for [%S] ONLINK ROUTE %d [%S]"), iScope[0], &iName, &logtmp, aRoute->iIndex, &logprf)); + // + // Create implicit host route entry, if ND is required on the interface + // (For non-ND interfaces, the route is used as is) + // + if (NeedsND() && !((TIp46Addr &)aDst).IsMulticast()) + { + CIp6Route *const n = GetRoute(aDst, 128, KRouteAdd_NEIGHBOR); + if (n == NULL) + return NULL; + if (n->iState == CIp6Route::EIncomplete) + n->StartND(aSrc); + LOG(Log::Printf(_L("\tIF %u [%S] NEXTHOP HOSTROUTE (ND ROUTE %u)"), iScope[0], &iName, n->iIndex)); + return n; + } + return aRoute; + } + +// +// CIp6Flow::SelectNextHop +// *********************** +void CIp6Flow::SelectNextHop() + { + const TIp6Addr &dst = iStart.ip6.DstAddr(); + // For log prints, have destination address as a string. + LOG(TLogAddressPrefix log_dst(dst)); + + // Assume iRoute determines the route and interface. Limit nexthop + // selection to that route only! + for (;/* JUST FOR BREAK EXITS */;) + { + if (iRoute == NULL) + { + LOG(Log::Printf(_L("\t\tFlow[%u] OOPS? Route is NULL for %S"), this, &log_dst)); + break; // -- no route attached + } + CIp6Interface &iface = iRoute->iInterface; + + if (!iStart.iSourceSet) + { + LOG(Log::Printf(_L("\t\tFlow[%u] IF %d [%S] has no source address for %S"), this, iface.iScope[0], &iface.iName, &log_dst)); + // If the source address cannot be assigned to this flow, + // punt the flow back to holding route (do not leave it + // attached to this interface). The reason is, for example + // PPP: + // - has a separate interface for IPv4 and IPv6 + // - if IPv6 interface installs default route before IPv4 + // has been configured, then IPv4 flows will try to + // attach to the IPv6 interface, but fail due to + // source address + // - however, if they are not moved to the holding route + // they are stuck forever into the IPv6, even after the + // real IPv4 becomes configured + // - *NOTE* IPv4 is just a case where a problem was observed, + // there may be other situations which would fail with IPv6 + // in similar ways (choosing wrong interface based on + // incomplete configuration), thus no special kludge + // just for IPv4 is not designed -- msa + // The "punt" has unfortunate side effect for ethernet: + // - when ethernet comes up and a flow is punted to the + // holding because DAD has not yet completed, the flow + // is not wakened when DAD completes, unless DAD completion + // also does a ScanHolding() [see Timeout()/DAD]. + // [Logically DAD completion should only be concern of + // flows attached to the interface, but this punting + // changes that... -- msa] + break; + } + if (iStart.iInterfaceIndex != iface.iScope[0]) + { + LOG(Log::Printf(_L("\t\tFlow[%u] Route lost, interface %u changed to IF %u [%S] for %S"), + this, iStart.iInterfaceIndex, iface.iScope[0], &iface.iName, &log_dst)); + break; // -- wrong interface (route was deleted probably) + } + CIp6Route *const route = iface.SelectNextHop(dst, iStart.ip6.SrcAddr(), iRoute); + if (iRoute == route) + return; // -- route not changed, all OK. + if (!route) + { + LOG(Log::Printf(_L("\t\tFlow[%u] IF %u [%S] has no ND entry for %S"), this, iface.iScope[0], &iface.iName, &log_dst)); + break; // -- no suitable route for the destination + } + // + // Route changed (into neighbor cache host route) + // + route->Attach(*this); + return; + } + iInterfacer.MoveToHolding(*this); + } + +// +// CIp6Interface::GetRoute +// *********************** +/** +// Insert a route entry to direct packets for a +// matching prefix to a specific named interface. +// (Interface is only activated after there a flow is +// activated to this route). +// +// A new route is NOT created if an identical route pointing +// to this same interface exists. In such case, the lifetime +// of the route is just updated (when lifetimes are implemented) +// It is allowed to have multiple routes with same prefix +// pointing to a *different* interface. +// +// NOTE: +// This "add front" feature is part of the specification +// and relied by some other parts. Beware of changing it! +// +// @return NULL or the pointer to the updated/created route entry +*/ +CIp6Route *CIp6Interface::GetRoute(const TIp6Addr &aAddr, TInt aPrefix, TUint aFlags, const TSockAddr *const aGateway, const TLifetime *const aLifetime) + { + CIp6Manager &mgr = Interfacer(); + const TLifetime lifetime = aLifetime ? *aLifetime : KLifetimeForever; + const TUint rtype = aFlags & (KRouteAdd_EXTENSIONMASK | KRouteAdd_TYPEMASK); + + // Initial state can only be (HOST, ONLINK, GATEWAY, MYPREFIX) with possible + // extension bits. In debug version, just panic the operation, if extra bits + // is present in the state. In production, the trunctated value is used as is. + ASSERT(rtype == (aFlags & KRouteAdd_STATEMASK)); + + const TLinkAddr KLinkAddr; + const TLinkAddr &gate = aGateway ? TLinkAddr::Cast(*aGateway) : KLinkAddr; + + TInt notifytype = EventTypeAdd; + + CIp6Route *rt; + for (CIp6Route **h = &iRouteList; ; h = &rt->iNext) + { + if ((rt = *h) == NULL) + { + // + // 1) Don't create entry with zero lifetime (call was just a Remove + // route request in disguise. + // 2) Don't create entry if the call was "update existing only" + // + if (lifetime == 0 || (aFlags & KRouteAdd_UPDATEONLY) != 0) + return NULL; + // + // This a new route, create it here + // + rt = new CIp6Route(++mgr.iRouteIndex, mgr, aAddr, aPrefix, *this); + if (rt == NULL) + return NULL; + rt->iState = (CIp6Route::TState)rtype; + + if (TIp46Addr::Cast(aAddr).IsMulticast()) + rt->iIsMulticast = 1; + rt->iIsProbing = (aFlags & KRouteAdd_PROBINGONLY) != 0; + break; + } + else if (rt->iLength == aPrefix && + rt->ExtendedType() == rtype && + rt->iPrefix.Match(aAddr) >= aPrefix && + // Require gateway match only for "true" gateway entries! + // (otherwise we never find the incomplete hostroutes, or + // changed link layer addresses, because aGateway does not + // match in those cases...). Note, specially that ERedirect + // gateways do not require match on address! + (rtype != CIp6Route::EGateway || rt->iAddress.Match(gate))) + { + // Somewhat dubious, but remove the the route from + // the current position (and it will be reinserted + // to the front. This might give priority to the last + // advertised default route, for example... -- msa + *h = rt->iNext; + // Should check the state matches the aFlags?? -- msa + + notifytype = EventTypeModify; + + if (lifetime == 0) + { + LOG(rt->LogRoute(0)); + // + // Delete matching route, if lifetime is ZERO + // + if (rt->iIsRouter) + { + rt->iIsRouter = 0; + RouterChanged(rt); + } + // + // If any flows are attached to the route that is being removed, + // move them all into the holding route with PENDING status. + // + // Note: holding is *ALWAYS* non-NULL. The only time holding + // can be NULL, is when it is being created by InitL(), and in + // that case GetRoute() *NEVER* gets into this branch! -- msa + // + mgr.MoveToHolding(*rt); + + // Send notification about removed route to event manager + NotifyRouteEvent(EventTypeDelete, rt); + + delete rt; + return NULL; + } + break; + } + } + + // + // Attach the route (new or some old) to front of the route list + // + rt->iNext = iRouteList; + iRouteList = rt; + + // + // Load gateway address, if specified + // + if (rt->Update(aFlags, aGateway, aLifetime) || notifytype == EventTypeAdd) + { + LOG(rt->LogRoute(lifetime)); + rt->NotifyFlows(EFlow_READY); + + // Send notification about new route to event manager + NotifyRouteEvent(notifytype, rt, lifetime); + } + + return rt; + } + +void CIp6Interface::NotifyRouteEvent(TUint aEventType, const CIp6Route *aRoute, + const TLifetime /*aLifetime*/) const +/** +Sends notification about event on routing table (add, delete, change) to event manager. +Event manager distributes the event to interested plugins. +This method handles two classes of notifications EClassRoute for events on route information, +and EClassNeighbour for events on changed information in neighbour cache. + +@param aEventType EventTypeAdd, EventTypeDelete, or EventTypeModify. +@param aRoute Pointer to routing table entry that has changed. +@param aLifetime Not used. Will be removed. +*/ + { + CIp6Manager &mgr = Interfacer(); + TUint evclass = aRoute->IsHostRoute() ? EClassNeighbour : EClassRoute; + + // If there is no event manager, or if there are no registered listeners, we can exit + // the function right away + if (!mgr.EventManager()) + { + return; + } + + if (mgr.EventManager()->IsEmpty(evclass)) + { + return; + } + + void *ptr = NULL; // Either TInetRouteInfo or TInetNeighbourInfo + TTime stamp; + stamp.UniversalTime(); + TInetRouteInfo rinfo; + TInetNeighbourInfo nginfo; + if (evclass == EClassRoute) + { + aRoute->FillRouteInfo(rinfo, Elapsed(stamp)); + ptr = &rinfo; + } + else + { + aRoute->FillNeighbourInfo(nginfo, Elapsed(stamp)); + ptr = &nginfo; + } + + mgr.EventManager()->Notify(evclass, aEventType, ptr); + } + + +// CIp6Manager::AddRouteL +// ********************** +// This creates both route and interface (if interface didn't exist before) +void CIp6Manager::AddRouteL + (const TIp6Addr &aAddr, TInt aPrefix,const TDesC &aName, TUint aFlags, + const TSockAddr *const aGateway, const TUint32 *const aLifetime) + { + CIp6Interface *const iface = GetInterfaceByNameL(aName); + const CIp6Route *const route = iface->GetRoute(aAddr, aPrefix, aFlags, aGateway, aLifetime); + if (route) + { + if (route->IsMyPrefix()) + // FIXME: Temporary fix to make node-local multicast through loopback interface to work + iface->AddId(aAddr, aPrefix < 128 ? 128 : 0); // (attempt to make at least one id implicitly defined + // so that a single AddRouteL can be used to create + // a "virtual" or loopback interface. + ScanHoldings(); // ...routes might have changed by above + } + } + +// CIp6Manager::CheckRoute +// *********************** +/** +// Find a route by destination address and get a matching source address. +// @return +// @li KErrNone, if route and source address found +// @li KErrNotFound, otherwise +*/ +TInt CIp6Manager::CheckRoute(const TIp6Addr &aAddr, const TUint32 aScopeId, TIp6Addr &aSrc) const + { + const CIp6Route *const route = FindRoute(aAddr, aScopeId, (TUint)(aAddr.Scope()-1)); + if (route && route->iInterface.SelectSource(aSrc, aAddr)) + return KErrNone; // Route and source address found! + return KErrNotFound; // Route or source address not found! + } + +// +// CIp6Interface::RemoveRoute +// ************************** +// Remove a specific route +void CIp6Interface::RemoveRoute(CIp6Route *aRoute) + { + CIp6Route **h, *rt; + for (h = &iRouteList; (rt = *h) != NULL; h = &rt->iNext) + { + if (rt == aRoute) + { + *h = rt->iNext; + LOG(rt->LogRoute(0)); + + if (rt->iIsRouter) + { + rt->iIsRouter = 0; + RouterChanged(rt); // iIsRouter: "1 -> 0" + } + // Holding route should really never be deleted through this! + ASSERT(rt != Interfacer().iHoldingRoute); + // Punt flows into holding for next hop selection (need ScanHolding?) + if (rt != Interfacer().iHoldingRoute) + Interfacer().MoveToHolding(*rt); + + // Send notification to event manager + NotifyRouteEvent(EventTypeDelete, rt); + + delete rt; + break; + } + } + } + +// CIp6Interface::MoveToFront +// ************************** +/** +// Move the specific route to the first in the list. The +// route SHOULD be in the list, but if not, nothing happens +// +// (currently only used by select next hop, in rare occasions) +*/ +void CIp6Interface::MoveToFront(CIp6Route *aRoute) + { + CIp6Route **h, *rt; + for (h = &iRouteList; (rt = *h) != NULL; h = &rt->iNext) + { + if (rt == aRoute) + { + *h = rt->iNext; + rt->iNext = iRouteList; + iRouteList = rt; + break; + } + } + } + +// +// CIp6Manager::LocalScope +// *********************** +/** +// @returns non-zero scope id, if aAddr is a valid source address +// for packets originating from this node, +// and ZERO otherwise. +*/ +TUint32 CIp6Manager::LocalScope(const TIp6Addr &aAddr, const TUint32 aLock, const TScopeType aLockType) const + { + const TUint scope = (TUint)(aAddr.Scope()-1); + if (scope > EScopeType_NET) + return 0; // Bad Address + + for (const CIp6Interface *iface = iInterfaceList;iface != NULL; iface = iface->iNext) + { + if ((aLock == 0 || iface->iScope[aLockType] == 0 || iface->iScope[aLockType] == aLock) && + iface->IsMyAddress(aAddr)) + // iface->iScope[scope] can be ZERO for looback (and similar *wild* + // interface). Return the default non-zero ID value in such case. + // (it will match this same network). + return iface->iScope[scope] ? iface->iScope[scope] : KDefaultNetworkId; + } + return 0; + } + +// +// CIp6Manager::RemoteScope +// ************************ +// Determine the default scope id for a destination address. +// +// *NOTE* This does not check whether a matching interface actually exists! +// +TUint32 CIp6Manager::RemoteScope(const TIp6Addr &aAddr, const TUint32 aLock, const TScopeType aLockType) const + { + const CIp6Route *const rt = FindRoute(aAddr, aLock, aLockType); + if (rt == NULL) + return 0; + + // If rt non-null, then the scope index below will be valid (no need to check!) + const TUint scope_id = rt->iInterface.iScope[aAddr.Scope() - 1]; + + // iface can be ZERO for looback (and similar *wild* + // interface). Return the default non-zero ID value in such case. + // (it will match this same network). + return scope_id ? scope_id : KDefaultNetworkId; + } + +// CIp6Manager::IsForMe +// ******************** +// (private help utility) +// +// @returns +// @li != 0 (= interface index), if address is for me +// @li == 0, if address is not for me +// +#ifdef WEAK_ES +TUint32 CIp6Manager::IsForMe + (const TIp6Addr &aAddr, const CIp6Interface *const aSrcIf, const TUint32 aScopeId, const TScopeType aType) const +#else +TUint32 CIp6Manager::IsForMe(const TIp6Addr &aAddr, const CIp6Interface *const aSrcIf) const +#endif + { + // + // Check the original interface first, as this is the + // most common case. + // + if (aSrcIf->IsForMeAddress(aAddr)) + return aSrcIf->iScope[0]; + +#ifdef WEAK_ES + // + // In weak ES model, incoming packet can be accepted, even + // if destination address is assigned to another interface + // + // *NOTE* + // the scope check does limit the range! So, this is not + // exactly the weak model either. + // + // *NOTE* + // the scope test is for strict equality! Thus, this WILL not + // match addresses on loopback interfaces (unless the + // original incoming interface was also a loopback--in + // which case it should have already been dealt in above + // test for original interface!) + // + for (const CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + if (iface->iScope[aType] == aScopeId && iface != aSrcIf && iface->IsForMeAddress(aAddr)) + return iface->iScope[0]; +#endif + return 0; + } + +// +// CIp6Manager::IsForMeAddress +// *************************** +/** +// Returns the interface index, if aAddr selects the current node +// (packets having this address as a destination are intended for me) +// +// @returns +// @li != 0 (= interface index), if address is for me +// @li == 0, if address is not for me +*/ +TUint32 CIp6Manager::IsForMeAddress(const TIp6Addr &aAddr, const TUint32 aInterfaceIndex) const + { + if (aInterfaceIndex == 0) + { + for (const CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + if (iface->IsForMeAddress(aAddr)) + return iface->iScope[0]; + return 0; + } + + const CIp6Interface *const ifp = FindInterface(aInterfaceIndex); + if (ifp == NULL) + return 0; // Cannot locate interface! +#ifdef WEAK_ES + const TUint scope = (TUint)(aAddr.Scope() - 1); + if (scope > EScopeType_NET) + return 0; // Invalid address + return IsForMe(aAddr, ifp, ifp->iScope[scope], (TScopeType)scope); +#else + return IsForMe(aAddr, ifp); +#endif + } + +// CIp6Manager::IsForMePacket +// ************************** +/** +// Complete scopes into the info and check if received packet as described by aInfo is for me. +// +// @return +// @li -1 if incoming interface or addresses are not valid (drop packet!) +// @li =0 if packet is not for me +// @li =1 if packet is for me +*/ +TInt CIp6Manager::IsForMePacket(RMBufRecvInfo &aInfo) const + { + const CIp6Interface *const ifp = FindInterface(aInfo.iInterfaceIndex); + if (ifp == 0) + return -1; // Cannot locate interface! + + LOG(PktLog(_L("\tIF %u [%S] RECV prot=%d src=%S dst=%S len=%d"), aInfo, ifp->iScope[0], ifp->iName)); + + const TIp6Addr &src = TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address(); + const TIp6Addr &dst = TInetAddr::Cast(aInfo.iDstAddr).Ip6Address(); + + { + // + // Fix source scope id into info block + // + const TUint scope = src.Scope() - 1; + if (scope > EScopeType_NET) + return -1; // Invalid source scope + else if (scope == EScopeType_IF && + !TIp46Addr::Cast(src).IsUnspecified() && + !ifp->IsMyAddress(src)) + return -1; // Invalid source address (loopback address from wrong interface!) + TInetAddr::Cast(aInfo.iSrcAddr).SetScope(ifp->iScope[scope]); + } + // + // Fix destination scope id into info block + // + const TUint scope = (TUint)(dst.Scope() - 1); + if (scope > EScopeType_NET) + return -1; // Invalid destination scope + const TUint dstId = ifp->iScope[scope]; + TInetAddr::Cast(aInfo.iDstAddr).SetScope(dstId); + +#ifdef WEAK_ES + return IsForMe(dst, ifp, dstId, (TScopeType)scope) != 0; +#else + return IsForMe(dst, ifp) != 0; +#endif + } + +// MagicSetAddress +// *************** +// A static help routine to load TSockAddr address from a prefix +// +static void MagicSetAddress(TSockAddr &aAddr, const TIp6Addr &aSrc, TUint aLength, TSockAddr *aMask = NULL) + { + // + // Make aAddr address family to either IPv4 or IPv6 depending + // on whether aSrc is V4 mapped address or not. + // + if (aSrc.IsV4Mapped()) + { + ((TInetAddr &)aAddr).SetAddress( + (aSrc.u.iAddr8[12] << 24) | + (aSrc.u.iAddr8[13] << 16) | + (aSrc.u.iAddr8[14] << 8) | + aSrc.u.iAddr8[15]); + if (aMask) + { + const TInt shift = 32 - (aLength - 96); + ((TInetAddr *)aMask)->SetAddress(shift > 31 ? 0 : ~0L << shift); + } + } + else + { + ((TInetAddr &)aAddr).SetAddress(aSrc); + if (aMask) + ((TInetAddr *)aMask)->PrefixMask(aLength); + } + } + +// MagicGetAddress +// *************** +/** +// Convert Address/Mask pair into Address and prefix, while doing some +// IPv4/IPv6 unifications. +// +// @returns the prefix length (the number of leftmost 1-bits in the mask) +// @li < 0, (KErrArgument) the address family of addr or mask is not KAfInet6, +// KAfInet or Unspecified +// @li = 0, if mask is unspecified (or if leftmost bit is zero) +// @li = 1..128, the prefix length +// @li = 129 addr is unspecified family (mask is ignored) +*/ +static TInt MagicGetAddress(TIp6Addr &aResult, const TInetAddr &aAddr, const TInetAddr &aMask) + { + // + // Convert aAddr into plain IPv6 address + // + if (aAddr.Family() == KAfInet) + { + TInetAddr tmp(aAddr); + tmp.ConvertToV4Mapped(); + aResult = tmp.Ip6Address(); + } + else if (aAddr.Family() == KAfInet6) + aResult = aAddr.Ip6Address(); + else if (aAddr.Family() == KAFUnspec) + return 129; // Special value to indicate: no address/prefix present + else + return KErrArgument; // Invalid Address + // + // Convert aMask into prefix length (ugh!) + // + if (aMask.Family() == KAfInet) + return 96 + MaskLength(aMask.Address()); + else if (aMask.Family() == KAfInet6) + return MaskLength(aMask.Ip6Address()); + else if (aMask.Family() == KAFUnspec) + return 0; + return KErrArgument; // Invalid aMask! + } + + +void CIp6Route::FillRouteInfo(TInetRouteInfo &rinfo, TLifetime aReftime) const +/** +Fill TInetRouteInfo struct based on this route. + +@param rinfo struct to be filled by the route information. +@param aRefTime reference time used in lifetime calculation. It should refer to the time + the network interface has been up. +*/ + { + rinfo.iType = 0; + rinfo.iState = iState; + rinfo.iMetric = iMetric; + rinfo.iDstAddr = iPrefix; + rinfo.iPrefixLen = iLength; + rinfo.iInterface = iInterface.Index(); + rinfo.iScopeId = iInterface.Scope((TScopeType)(iPrefix.Scope()-1)); + rinfo.iGateway = iAddress.Ip6Address(); + rinfo.iIndex = iIndex; + + if (iLifetime.iPreferred != KLifetimeForever) + { + rinfo.iLifetime = (iLifetime.iPreferred > aReftime) ? + iLifetime.iPreferred - aReftime : 0; + } + else + { + rinfo.iLifetime = KLifetimeForever; + } + + if (iLifetime.iDeprecated) + rinfo.iType |= (TUint32)TInetRouteInfo::EDeprecated; + } + + +void CIp6Route::FillNeighbourInfo(TInetNeighbourInfo &nginfo, TLifetime aReftime) const +/** +Fill TInetNeighbourInfo struct based on this route. + +@param rinfo struct to be filled by the neighbour information. +@param aRefTime reference time used in lifetime calculation. It should refer to the time + the network interface has been up. +*/ + { + nginfo.iIndex = iIndex; + nginfo.iState = iState; + nginfo.iMetric = iMetric; + nginfo.iDstAddr = iPrefix; + nginfo.iInterface = iInterface.Index(); + nginfo.iScopeId = iInterface.Scope((TScopeType)(iPrefix.Scope()-1)); + nginfo.iHwAddr.Copy(iAddress.Address()); + + if (iLifetime.iPreferred != KLifetimeForever) + { + nginfo.iLifetime = (iLifetime.iPreferred > aReftime) ? + iLifetime.iPreferred - aReftime : 0; + } + else + { + nginfo.iLifetime = KLifetimeForever; + } + } + + +void CIp6Interface::SetAddressAndScope(TSockAddr &aAddr, const TSockAddr &aSrc) const + /** + * Assign an address (aSrc to aAddr) and supplement it with the + * scope information from the interface. + * + * @retval aAddr The address supplemented with the scope id. + * @param aSrc The address to be supplemented. + * + * @note aSrc and aAddr can reference the same address. + */ + { + // Allow call with aAddr == aSrc. Testing this may not be + // necessary, but just to be sure, avoid "overlapping" + // copy operation in such case... -- msa + if (&aAddr != &aSrc) + aAddr = aSrc; + + TInetAddr &addr = TInetAddr::Cast(aAddr); + if (addr.Family() == KAfInet) + addr.ConvertToV4Mapped(); + if (addr.Family() == KAfInet6) + { + // + // Scope is stored only if the address family is KAfInet or KAfInet6, + // and if so, the final family is always KAfInet6. + const TUint scopeType = (TUint)(addr.Ip6Address().Scope() - 1); + if (scopeType > EScopeType_NET) + return; + addr.SetScope(iScope[scopeType]); + // + // (minor feature: if scope id would be 0, return IPv4 as KAfInet) + // [maybe of dubious value, just always return Ipv6 format?] + if (iScope[scopeType] == 0 && addr.IsV4Mapped()) + addr.ConvertToV4(); + } + } + +// CIp6Manager::InterfaceInfo +// ************************** +/** +// Locate the next interface after aIndex and return the +// information and assigned interface index. +// +// @return +// @li = 0, if no next interface exists +// @li > 0, interface index, aInfo updated to describe this interface +*/ +TUint CIp6Manager::InterfaceInfo(TUint aIndex, TSoInetInterfaceInfo &aInfo) const + { + // ..yes, this is silly O(n!) (?) algorithm for scanning the interfaces. Each time + // this is called, it has find and count all entries that come before the specified + // aIndex. + // Also, if between calls, prefixes or id's have been added or removed, the algorithm + // fails and returns same entries multiple times or skips some that it should have + // returned. However, this should rarely happen. + TUint i = 0; + for (const CIp6Interface *iface = iInterfaceList;iface != NULL; iface = iface->iNext) + { + TInt found = 0; + for (const TIp6AddressInfo *address = &iface->iAddress; ; address = &address->iNext->iInfo) + { + TIp6Addr addr(KInet6AddrNone); + TInt length = 0; + // "i" represents a virtual index over all *assigned* addresses in all interfaces, + // with exception that at least one entry is returned for each interface, whether + // there is assigned addresses or not. + if (!address->IsSet()) + { + if (address->iNext) + continue; // Check next address. + // + // This is the last address (actually, this is the primary address slot) + // + if (found > 0 || ++i <= aIndex) + break; // Done with this interface. + // No entries returned from this interface, + // return one dummy with no address + } + else if (address->iPrefix == 0) + { + // + // Specified individual alias address + // + ++found; + if (++i <= aIndex) // Already processed? + { + if (address->iNext) + continue; // Look next address. + else + break; // Last address, look for next interface + } + // Use address as is... + addr = address->iId; + } + else + { + // + // Address is specified as autoconfigured ID part, find out the next + // prefix to combine with it. + // + // coverity[write_write_order] + const CIp6Route *route = route = iface->iRouteList; + for (; route != NULL; route = route->iNext) + { + if (!route->IsMyPrefix()) + continue; + // The lengths of the prefix and id must be compatible to be + // combined. + if (route->iLength != address->iPrefix) + continue; + + found++; + if (++i > aIndex) + break; // a prefix found! + // this prefix was already processed look forward... + } + if (route == NULL) + { + // No unprocessed prefixes, go to next address, if any + if (address->iNext) + continue; + else + { + if (found > 0 || ++i <= aIndex) + break; // Done with this interface. + // No entries returned from this interface, + // return one dummy with no address (or whatever + // the current address points to). + addr = address->iId; + } + } + else + { + // + // Combine prefix and current id part into full address + // + addr = route->iPrefix; + length = route->iLength; + MakeFullAddress(addr, length, address->iId.u.iAddr8, sizeof(address->iId.u.iAddr8)); + } + } + // + // "Interface" located, return information about it + // + aInfo.iName = iface->iName; + if (iface->iNifIf == NULL || !address->IsSet()) + aInfo.iState = EIfDown; // no interface or address not known or was duplicate + else if (iface->iState == EFlow_READY) + aInfo.iState = EIfUp; + else if (iface->iState == EFlow_PENDING) + aInfo.iState = EIfPending; + else if (iface->iState == EFlow_HOLD) + aInfo.iState = EIfBusy; + else + aInfo.iState = EIfDown; + aInfo.iTag = iface->iName; // dubious, but what else? -- msa + aInfo.iMtu = iface->iSMtu; + aInfo.iSpeedMetric = iface->iSpeedMetric; + aInfo.iFeatures = iface->iFeatures; + aInfo.iHwAddr = iface->iHwAddr; + + iface->SetAddressAndScope(aInfo.iNameSer1, iface->iNameSer1); + iface->SetAddressAndScope(aInfo.iNameSer2, iface->iNameSer2); + + TInetAddr none; + if (addr.IsV4Mapped()) + { + // IPv4 interface + none.SetAddress(0); + // + // Get some configuration information + // (ignore errors and assume the 'cfg' is + // mostly initialized to null addresses in such case) + // + TPckgBuf cfg; + (void)GetIp4Config(iface->iNifIf, cfg); + const TSoInetIfConfig &cf = cfg(); + ((TInetAddr &)aInfo.iNetMask) = cf.iConfig.iNetMask; + iface->SetAddressAndScope(aInfo.iBrdAddr, cf.iConfig.iBrdAddr); + } + else + { + // IPv6 interface + none.SetAddress(KInet6AddrNone); + ((TInetAddr &)aInfo.iNetMask) = none; + ((TInetAddr &)aInfo.iBrdAddr) = none; + } + + // default gateway + TInetAddr gw; + iface->GetDefGateway(addr.IsV4Mapped(), gw); + iface->SetAddressAndScope(aInfo.iDefGate, gw); + + ((TInetAddr &)aInfo.iAddress) = none; + MagicSetAddress(aInfo.iAddress, addr, length, &aInfo.iNetMask); + iface->SetAddressAndScope(aInfo.iAddress, aInfo.iAddress); + return i; + } + } + return 0; + } + + +TInt CIp6Manager::GetInterfaces(TDes8& aOption) const + { + TOverlayArray info(aOption); + TInt idx = 0, exceed = 0; + const CIp6Interface *iface = iInterfaceList; + + while (iface) + { + // IndexPtr returns NULL, if there is not enough room in descriptor + if (info.IndexPtr(idx)) + { + TInetInterfaceInfo *const opt = new (info.IndexPtr(idx)) TInetInterfaceInfo; + opt->iIndex = iface->iScope[0]; + opt->iName = iface->iName; + opt->iState = iface->iState; + opt->iSMtu = iface->iSMtu; + opt->iRMtu = iface->iRMtu; + opt->iSpeedMetric = iface->iSpeedMetric; + opt->iFeatures = iface->iFeatures; + opt->iHwAddr = iface->iHwAddr; + + idx++; + } + else + { + exceed++; + } + iface = iface->iNext; + } + + info.SetLength(idx); + + return exceed; + } + + +TInt CIp6Manager::GetAddresses(TDes8 &aOption) const + { + TOverlayArray info(aOption); + TInt idx = 0, excess = 0, count = 0; + #ifdef _DEBUG + TInt ifaceSpecificAddrIdx = 0; + #endif + + for (const CIp6Interface *iface = iInterfaceList;iface != NULL; iface = iface->iNext) + { + #ifdef _DEBUG + ifaceSpecificAddrIdx = 0; + #endif + + TTime stamp; + stamp.UniversalTime(); + const TLifetime current_time = iface->Elapsed(stamp); + + for ( const TIp6AddressInfo *address = &iface->iAddress; + address != NULL; + address = &address->iNext->iInfo) + { + TIp6Addr addr(KInet6AddrNone); + + // Own prefixes are appended with an ID part used for the interface and included in + // address list. They are stored in route table with "myprefix" flag. + // Route table is maintained separately for each interface, hence the multilevel loop + const CIp6Route *route = iface->iRouteList; + for (;;) + { + TBool haveprefix = EFalse; + + if (route && !route->IsMyPrefix()) + { + route = route->iNext; + continue; + } + + // The lengths of the prefix and id must be compatible to be + // combined. + if (route && route->iLength != address->iPrefix) + { + route = route->iNext; + continue; + } + + if (route == NULL || address->iPrefix == 0) + { + addr = address->iId; + } + else + { + // Combine prefix and current id part into full address + addr = route->iPrefix; + haveprefix = ETrue; + MakeFullAddress(addr, route->iLength, address->iId.u.iAddr8, sizeof(address->iId.u.iAddr8)); + } + + if (info.IndexPtr(idx)) + { + #ifdef _DEBUG + if( !address->iId.IsUnspecified() ) + { + if( ifaceSpecificAddrIdx == 0 ) + { + ASSERT( address->IsPrimary() ); + } + else + { + ASSERT( !address->IsPrimary() ); + } + } + #endif + + TInetAddressInfo *const opt = new (info.IndexPtr(idx)) TInetAddressInfo; + + opt->iInterface = iface->iScope[0]; + opt->iAddress = addr; + opt->iPrefixLen = address->iPrefix; + opt->iGenerations = address->iGenerated; + opt->iNS = address->iNS; + opt->iState = address->AddressState(); + opt->iType = address->AddressType(); + opt->iFlags = 0; + + // We distinct the "ID"-entries from "prefix" entries for the user. + // The actual set of usable addresses is the set of prefix x id combinations. + // This has more significance when handling address events rather than here, + // but I'd like to do it for completeness + if (!haveprefix) + { + opt->iFlags |= TInetAddressInfo::EF_Id; + } + else + { + opt->iFlags |= TInetAddressInfo::EF_Prefix; + } + + TScopeType st = (TScopeType) (addr.Scope() - 1); + opt->iScopeId = iface->Scope(st); + + TLifetime plt = KLifetimeForever, vlt = KLifetimeForever; + + // Lifetimes relate to the interface startup time + if (haveprefix) + { + if (route->iLifetime.iPreferred != KLifetimeForever) + { + plt = (route->iLifetime.iPreferred > current_time) ? + route->iLifetime.iPreferred - current_time : 0; + } + if (route->iLifetime.iPreferred != KLifetimeForever && + route->iLifetime.iStored != KLifetimeForever) + { + vlt = (route->iLifetime.iStored > current_time) ? + route->iLifetime.iStored - current_time : 0; + } + + if (route->iLifetime.iDeprecated) + { + opt->iFlags |= TInetAddressInfo::EF_Deprecated; + } + } + else + { + // Address lifetimes are computed in timer units! + const TLifetime current_age = ElapsedUnits(address->iCreated, stamp); + + if (address->iPLT != KLifetimeForever) + { + plt = (address->iPLT > current_age) ? + address->iPLT - current_age : 0 /* 0 = expired*/; + plt /= TIMER_UNIT; + } + + if (address->iVLT != KLifetimeForever) + { + vlt = (address->iVLT > current_age) ? + address->iVLT - current_age : 0 /* 0 = expired*/; + vlt /= TIMER_UNIT; + } + + if (plt == 0) + { + opt->iFlags |= TInetAddressInfo::EF_Deprecated; + } + } + + opt->iPrefLifetime = plt; + opt->iValidLifetime = vlt; + count++; + } + else + { + excess++; + } + + idx++; + if (!route) break; + route = route->iNext; + + // Must try one time with route == NULL to output a possible pure Id entry + } + + if (!address->iNext) + { + break; + } + + #ifdef _DEBUG + ifaceSpecificAddrIdx++; + #endif + } + } + + info.SetLength(count); + + // Return number of addresses not fit into the option buffer. 0 indicates all of them are + // listed + return excess; + } + + +TInt CIp6Manager::GetRoutes(TDes8& aOption) const + { + TOverlayArray info(aOption); + TInt idx = 0, exceed = 0; + + for (const CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + { + for (const CIp6Route *route = iface->iRouteList; route != NULL; route = route->iNext) + { + // Ignore myprefix entries, these are listed in GetAddresses() + if (route->IsMyPrefix()) + { + continue; + } + + if (idx < info.MaxLength()) + { + TTime stamp; + stamp.UniversalTime(); + TInetRouteInfo *const rinfo = new (info.IndexPtr(idx)) TInetRouteInfo; + route->FillRouteInfo(*rinfo, iface->Elapsed(stamp)); + idx++; + } + else + { + exceed++; + } + } + } + + info.SetLength(idx); + + return exceed; + } + + +// CIp6Manager::RouteInfo +// ********************** +/** +// Locate the next route after aIndex and return the +// information and assinged route index. +// +// @return +// @li = 0, if not next route exits +// @li > 0, route index, aInfo updated to describe this route +*/ +TUint CIp6Manager::RouteInfo(TUint aIndex, TSoInetRouteInfo &aInfo) const + { + const CIp6Route *rt = NULL; + const CIp6Interface *ifp; + for (ifp = iInterfaceList;; ifp = ifp->iNext) + { + if (ifp == NULL) + return 0; // No more routes; + + for (rt = ifp->iRouteList; rt != NULL; rt = rt->iNext) + if (rt->iIndex > aIndex && + !rt->IsMyPrefix()) // ..additional condition: ingore "prefix" entries in the list! + goto found_one; + } + // At least for now, the route list is *NOT* ordered by + // the index. Thus, need to find a next higher index by + // scanning the full list! +found_one: // ifp != NULL && rt != NULL + const CIp6Route *next = rt; + for (;;) + { + for (;rt; rt = rt->iNext) + { + if (rt->iIndex > aIndex && rt->iIndex < next->iIndex && + !rt->IsMyPrefix()) // ..additional condition: ingore "prefix" entries in the list! + next = rt; + } + if ((ifp = ifp->iNext) == NULL) + break; + rt = ifp->iRouteList; + } + + TIp6Addr ifaddr(KInet6AddrNone); + + // The existing values of TRouteState and TRouteType are not + // quite suitable for the current IPv6 Neighbor Discovery + // environment. Just do some reasonable effort in mapping + // the route state into iState & iType. + // + // - if route is controlled by ND, set type = ERtIcmpAdd + // + aInfo.iType = ERtNormal; + aInfo.iState = ERtReady; + switch (next->iState) + { + case CIp6Route::EIncomplete: + aInfo.iType = ERtIcmpAdd; + aInfo.iState = ERtPending; + break; + case CIp6Route::EReachable: + case CIp6Route::EStale: + case CIp6Route::EDelay: + case CIp6Route::EProbe: + aInfo.iType = ERtIcmpAdd; + aInfo.iState = ERtReady; + break; + default: + break; + } + aInfo.iMetric = next->iMetric; + + (void)next->iInterface.SelectSource(ifaddr, next->iPrefix); + + MagicSetAddress(aInfo.iIfAddr, ifaddr, 0); + next->iInterface.SetAddressAndScope(aInfo.iIfAddr, aInfo.iIfAddr); + if (next->iState == CIp6Route::ELoopback) + aInfo.iGateway.SetFamily(0); + else + { + next->iAddress.GetAddress(aInfo.iGateway); + next->iInterface.SetAddressAndScope(aInfo.iGateway, aInfo.iGateway); + } + MagicSetAddress(aInfo.iDstAddr, next->iPrefix, next->iLength, &aInfo.iNetMask); + return next->iIndex; + } + +// CIp6Manager::InterfaceQueryOption +// ********************************* +// Get information about interface +// +TInt CIp6Manager::InterfaceQueryOption(TUint aName, TSoInetIfQuery &aQuery, const TInt aLength) const + { + // aLength holds the availabe space for iZone[] array (backward compatibility kludge for + // some odd applications that might be using old in_sock.h without the iZone part! + if (aLength < 0) + return KErrArgument; // Option is too short to be anything sensible! + + const CIp6Interface *iface = NULL; + + // + // the destination address is required in Ip6 format + // in processing, prefetch... + // + TIp46Addr dst(aQuery.iDstAddr); + // + // The aName determines how the interface is to be located + // + switch (aName) + { + case KSoInetIfQueryByDstAddr: + { + // + // *NOTE* This is not satisfactory! It will not give the right answer, + // if there are hooks that might change the interface based on destination + // address (or some policy). ...and if policies (IPSEC, QoS) come into + // picture, we need to have the protocol and port here too! + // To solve, need almost to create a flow and connect it, but not + // refresh (no ReadyL() phase, nor interface Activation!) + // + const CIp6Route *const route = FindRoute(dst, + aQuery.iDstAddr.Scope(), (TUint)(aQuery.iDstAddr.Ip6Address().Scope()-1)); + if (route) + iface = &route->iInterface; + break; + } + case KSoInetIfQueryBySrcAddr: + iface = FindInterface(aQuery.iSrcAddr); + break; + case KSoInetIfQueryByIndex: + iface = FindInterface(aQuery.iIndex); + break; + case KSoInetIfQueryByName: + iface = FindInterface(aQuery.iName); + break; + default: + return KErrNotSupported; + } + if (iface == NULL) + return KErrNotFound; + // + // Fill In the query information + // + // Try to select src address based on whatever content of dst has. If + // fails, then src address will be unspecified. + if (iface->SelectSource(dst, dst) != NULL) + aQuery.iSrcAddr.SetAddress(dst); + else + aQuery.iSrcAddr.Init(0); + // Zone ids, copy as much as there is available space in option. + const TInt N = (aLength > (TInt)sizeof(iface->iScope) ? sizeof(iface->iScope) : aLength) / sizeof(aQuery.iZone[0]); + for (int i = 0; i < N; ++i) + aQuery.iZone[i] = iface->iScope[i]; + aQuery.iIndex = iface->iScope[0]; // make sure iIndex is also correct. + aQuery.iName = iface->iName; // Interface Name + aQuery.iIsUp = iface->iNifIf != NULL; // 1 = if interface has CNifIfBase * attached + return KErrNone; + } + +// CIp6Manager::InetInterfaceOption +// ******************************** +// Modify Inet Interface information (SetOption part) +// +TInt CIp6Manager::InetInterfaceOption(TUint aName, const TSoInet6InterfaceInfo &aInfo) + { + #ifdef _LOG + TBuf<39> addressStr; + aInfo.iAddress.Output( addressStr ); + TBuf<39> netMaskStr; + aInfo.iNetMask.Output( netMaskStr ); + TBuf<39> brdAddrStr; + aInfo.iBrdAddr.Output( brdAddrStr ); + TBuf<39> defGateStr; + aInfo.iDefGate.Output( defGateStr ); + TBuf<39> nameSer1Str; + aInfo.iNameSer1.Output( nameSer1Str ); + TBuf<39> nameSer2Str; + aInfo.iNameSer2.Output( nameSer2Str ); + + LOG( Log::Printf( _L( "CIp6Interface::InetInterfaceOption iName(%S), iState(%d), iMtu(%d), iSpeedMetric(%d), iFeatures(%u), iAddress(%S), iNetMask(%S), iBrdAddr(%S), iDefGate(%S), iNameSer1(%S), iNameSer2(%S), iDelete(%u), iAlias(%u), iDoPrefix(%u), iDoId(%u), iDoState(%u), iDoAnycast(%u), iDoProxy(%u)" ), + &aInfo.iName, + aInfo.iState, + aInfo.iMtu, + aInfo.iSpeedMetric, + aInfo.iFeatures, + &addressStr, + &netMaskStr, + &brdAddrStr, + &defGateStr, + &nameSer1Str, + &nameSer2Str, + aInfo.iDelete, + aInfo.iAlias, + aInfo.iDoPrefix, + aInfo.iDoId, + aInfo.iDoAnycast, + aInfo.iDoProxy + ) ); + #endif + + // Locate the interface + CIp6Interface *iface = FindInterface(aInfo.iName); + if (iface) + { + switch (aName) + { + case KSoInetDeleteInterface: + RemoveInterface(iface); + return KErrNone; + case KSoInetConfigInterface: + case KSoInetChangeInterface: + break; + case KSoInetResetInterface: + // *THIS IMPLEMENATION NEEDS TO BE LOOKED INTO* -- msa + { + LOG(Log::Printf(_L("CIp6Manager::InetInterfaceOption(KSoInetResetInterface, %S)"), &iface->iName)); + if (iface->iState >= EFlow_READY) + iface->iState = EFlow_HOLD; + + for(CIp6Route *rt=iface->iRouteList; rt != NULL; rt = rt->iNext) + MoveToHolding(*rt); + iface->Reset(1); // Reset to initial state, but keep binding to NIF + } + return KErrNone; + case KSoInetStartInterface: + // *THIS IMPLEMENATION NEEDS TO BE LOOKED INTO* -- msa + LOG(Log::Printf(_L("CIp6Manager::InetInterfaceOption(KSoInetStartInterface, %S)"), &iface->iName)); + StartSending(iface->iNifIf); + return KErrNone; + case KSoInetCreateIPv4LLOnInterface: + { + LOG(Log::Printf(_L("CIp6Manager::InetInterfaceOption(KSoInetCreateIPv4LLOnInterface, %S)"), &iface->iName)); + + TInt err = KErrNone; + const TInt flag = iface->HaveIp4LinkLocal(); + + if( flag == CIp6Interface::EV4LLConfigDaemonControlled ) + { + // Check for any state change. + if (aInfo.iDoState) + { + if (aInfo.iState == EIfDown) + iface->iState = KErrNotReady; + else if (aInfo.iState == EIfUp) + iface->iState = EFlow_READY; + else + err = KErrArgument; // no others supported now! + } + + if( err == KErrNone ) + { + // Try to create a link local. + TInt retVal = iface->ConfigureLinkLocal( 0 ); + + // Check for any errors. + if( retVal == 1 ) + { + // Link local was created. + err = KErrNone; + + // Address configuration has been changed, the process completes + // through the Timeout function, activate it. + TTime stamp; + stamp.UniversalTime(); + iface->Timeout(stamp); + } + else + { + if( iface->HasIpv4LinkLocalAddr() ) + { + // Link local already exists. + err = KErrNone; + } + else if( !iface->iIsIPv4 ) + { + // This interface option is only available when using IPv4. + err = KErrNotSupported; + } + else + { + // Link local could not be created for unknown reasons. + err = KErrUnknown; + } + } + } + } + else + { + // This interface option is only available when EV4LLConfigDaemonControlled is specified. + err = KErrNotSupported; + } + + return err; + } + default: + return KErrNotSupported; + } + } + else if (aName == STATIC_CAST(TUint,KSoInetConfigInterface)) + { + TRAPD(err, iface = GetInterfaceByNameL(aInfo.iName)); + if (iface == NULL) + return err; + // For a newly created interface, let the address decide the "mode" + if (aInfo.iAddress.Family() == KAfInet6) + iface->iNifUser = iNifUser[E_IPv6]; + } + else + return KErrNotFound; + + // Execute the option on interface + + TIp6Addr addr; + TInt prefix = MagicGetAddress(addr, aInfo.iAddress, aInfo.iNetMask); + if (prefix < 0) + return prefix; // Bad address information + if (aInfo.iMtu > 0) + iface->iSMtu = iface->iRMtu = iface->iPMtu = aInfo.iMtu; + if (aInfo.iSpeedMetric > 0) + iface->iSpeedMetric = aInfo.iSpeedMetric; + if (aInfo.iDoState) + { + if (aInfo.iState == EIfDown) + iface->iState = KErrNotReady; + else if (aInfo.iState == EIfUp) + iface->iState = EFlow_READY; + else + return KErrArgument; // no others supported now! + } + iface->UpdateNameServers(aInfo.iNameSer1, aInfo.iNameSer2, 1); // Note, override mode! + + // + // Do some iDefGate processing (if specified) + // + if (!aInfo.iDefGate.IsUnspecified()) + { + // Family is either KAfInet or KAfInet6 now + TInetAddr defgate(aInfo.iDefGate); + if (defgate.Family() == KAfInet) + defgate.ConvertToV4Mapped(); + + static const TLifetime zero = 0; + const TLifetime *const lifetime = aInfo.iDelete ? &zero : NULL; + const TInt pb = defgate.IsV4Mapped() ? 96 : 0; + const TIp6Addr &gw = defgate.Ip6Address(); + if (gw.IsEqual(addr)) + (void)iface->GetRoute(gw, pb, KRouteAdd_ONLINK, NULL, lifetime); + else + { + (void)iface->GetRoute(gw, 128, KRouteAdd_ISROUTER, NULL, lifetime); + (void)iface->GetRoute(gw, pb, KRouteAdd_GATEWAY, &defgate, lifetime); + } + } + + if (prefix > 128) + return KErrNone; // No address info, ignore rest + + // + // prefix = 0 => request to configure a single address + // prefix > 0 => request to configure prefix and/or id part + // + if (aInfo.iDoId) + { + if (TIp46Addr::Cast(addr).IsMulticast()) + { +#if 0 + return KErrArgument; // Cannot configure multicast address as own address +#else + // + // Experimental hack -- treat multicast address configuration as + // multicast join/leave group event + // + return iface->UpdateMulticast(addr, aInfo.iDelete ? 0 : KLifetimeForever); +#endif + } + + // *NOTE* Some obscure stuff: You cannot configure a plain + // id, because this 'addr' is stored as is into the id list + // and the full address is used for DAD processing. + if (aInfo.iDelete) + { + const TInt err = iface->RemId(iface->GetId(addr)); + if (err != KErrNone) + return err; + } + else if (aInfo.iDoAnycast || aInfo.iDoProxy) + { + // + // Special addresses + // + const TInt address_type = + aInfo.iDoAnycast ? TIp6AddressInfo::EAnycast : + aInfo.iDoProxy ? TIp6AddressInfo::EProxy : + TIp6AddressInfo::ENormal; + (void)iface->AddId(addr, 0, address_type); + prefix = 0; // "disarm" iDoPrefix processing below (not relevant for proxy/anycast) + } + else if (addr.IsV4Mapped()) + { + // Configuring IPv4 interface with (prefix > 96) or without (prefix == 0) + // netmask. The "id" processing is adding the recognition of the network broadcast + // address. For IPv4, "alias" is always assumed implicitly + iface->ConfigureAddress(addr, prefix, ETrue); + prefix = 0; // "disarm" iDoPrefix processing below (not relevant for IPv4!) + } + else if (aInfo.iAlias || prefix == 0) + { + // when prefix==0, the request does not specify mask. Treat + // this as a request to add a special 128bit address as id + // (= configuring single address for interface). Do it + // always as "alias". + (void)iface->AddId(addr, prefix); + } + else + { + // Change/Define primary id part + iface->SetId(iface->iAddress, addr, prefix, TIp6AddressInfo::ENormal); + } + // + // Update lifetime and DAD events + // + TTime stamp; + stamp.UniversalTime(); + iface->Timeout(stamp); + } + // If prefix=0, AddId already does SetPrefix... + if (aInfo.iDoPrefix && prefix > 0) + { + // ...works as if RA prefix option with A=1 + const TLifetime lifetime = aInfo.iDelete ? 0 : KLifetimeForever; + iface->SetPrefix(addr, prefix, 1, lifetime); + // ...works as if RA prefix option with L=1 + iface->GetRoute(addr, prefix, KRouteAdd_ONLINK, NULL, &lifetime); + } + return KErrNone; + } + +// +// CIpManager::InterfaceOption +// *************************** +/** +// This method implements *BOTH* SetOption and GetOption when level KSOLInterface +// The call is translated to a Control(). Somewhat dubious, but thats the way it +// was before... +*/ +TInt CIp6Manager::InterfaceOption(TUint aLevel, TUint aName, TDes8 &aOption) const + { + // Note: Checking against MaxLength only to be sure that the iName field + // of TSoIfInfo is accessible. It just blindly assumed that the caller + // has initialized the content properly (even if Length() is not necessarily + // correctly set). [To be compatible with the old implementation]. + if ((TUint)aOption.MaxLength() < sizeof(TSoIfInfo)) + return KErrArgument; // Both Get/Set need the interface name! + const TSoIfInfo &opt = *(TSoIfInfo *)aOption.Ptr(); + + // Locate Interface mathing the specified name + const CIp6Interface *const iface = FindInterface(opt.iName); + if (iface) + return iface->iNifIf ? iface->iNifIf->Control(aLevel, aName, aOption) : KErrNotReady; + // No such interface + return KErrBadDriver; + } + + + +// +// CIp6Manager::MulticastOption +// **************************** +// Process Multicast Options Join and Leave Group +// +TInt CIp6Manager::MulticastOption(TUint aName, const TIp6Mreq &aRequest) + { + if (!TIp46Addr::Cast(aRequest.iAddr).IsMulticast()) + return KErrArgument; // This must be a valid multicast address! + CIp6Route *route; + // + // Locate interface to be used + // + CIp6Interface *iface; + if (aRequest.iInterface == 0) + { + route = FindRoute(aRequest.iAddr, 0, 0); + if (route == NULL) + return KErrNotFound; + iface = &route->iInterface; + } + else + { + // Assuming the request.iInterface is always a true interface index. + // (and not a scope id depending on the scope of the multicast address) + iface = FindInterface(aRequest.iInterface); + if (iface == NULL || (iface->FindRoute(aRequest.iAddr, NULL)) == NULL) + { + // Should return something specific: "no multicast enabled interface" or something... + // or, should this cause dialup popup? -- msa + return KErrNotFound; + } + } + // Note: the caller must have already verified that aName is either KSoIp6JoinGroup or KSoIp6LeaveGroup + return iface->UpdateMulticast(aRequest.iAddr, aName == KSoIp6JoinGroup ? KLifetimeForever : 0); + } + +// +// CIp6Manager::GetOption +// ********************** +TInt CIp6Manager::GetOption(TUint aLevel, TUint aName, TDes8 &aOption) const + { + return GetOption(aLevel, aName, aOption, *(CIp6Manager *)this); + } + +TInt CIp6Manager::GetOption(TUint aLevel, TUint aName, TDes8 &aOption, MProvdSecurityChecker &aChecker) const + { + if (aLevel == KSOLInterface) + { + const TInt ret = aChecker.CheckPolicy(KPolicyNetworkControl, 0); + if (ret != KErrNone) + return ret; + return InterfaceOption(aLevel, aName, aOption); + } + + else if (aLevel == KSolInetIfQuery) + { + // Returns an array of TInetInterfaceInfo objects + if (aName == KSoInetInterfaceInfo) + { + return GetInterfaces(aOption); + } + + // Returns an array of TInetAddressInfo objects + else if (aName == KSoInetAddressInfo) + { + return GetAddresses(aOption); + } + + // Returns an array of TInetRouteInfo objects + else if (aName == KSoInetRouteInfo) + { + return GetRoutes(aOption); + } + + // Other options return TSoInetIfQuery object + return InterfaceQueryOption(aName, *(TSoInetIfQuery *)aOption.Ptr(), aOption.Length() - _FOFF(TSoInetIfQuery, iZone[0])); + } + return KErrNotSupported; // No get options supported for now (here) + } + +// CIp6Manager::SetOption +// ********************** +TInt CIp6Manager::SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption) + { + return SetOption(aLevel, aName, aOption, *this); + } + +TInt CIp6Manager::SetOption(TUint aLevel, TUint aName, const TDesC8 &aOption, MProvdSecurityChecker &aChecker) + { + const TUint8 &ref = *aOption.Ptr(); // Prefetch for use in below. + + if (aLevel == KSolInetIp) + { + if (aName == KSoIp6JoinGroup || aName == KSoIp6LeaveGroup) + { + if (aOption.Length() != sizeof(TIp6Mreq)) + return KErrArgument; + return MulticastOption(aName, (TIp6Mreq &)ref); + } + return KErrNotSupported; + } + + if (aLevel == KSolInetIfCtrl) + { + const TInt ret = aChecker.CheckPolicy(KPolicyNetworkControl, 0); + if (ret != KErrNone) + return ret; + if (aName == KSoIpv4LinkLocal && + aOption.Length() == sizeof(TSoInetIpv4LinkLocalInfo)) + return SetIpv4LinkLocalOption((TSoInetIpv4LinkLocalInfo &)ref); + + if (aOption.Length() != sizeof(TSoInet6InterfaceInfo)) + return KErrArgument; + return InetInterfaceOption(aName, (TSoInet6InterfaceInfo &)ref); + } + else if (aLevel == KSOLInterface) + { + const TInt ret = aChecker.CheckPolicy(KPolicyNetworkControl, 0); + if (ret != KErrNone) + return ret; + // InterfaceOption needs modifiable descriptor, make it so! + TPtr8 tmp((TUint8 *)&ref, aOption.Length()); + return InterfaceOption(aLevel, aName, tmp); + } + else if (aLevel == KSolInetIfQuery) + { + const TInt ret = aChecker.CheckPolicy(KPolicyNetworkControl, 0); + if (ret != KErrNone) + return ret; + if (aOption.Length() != sizeof(TSoInetIfQuery)) + return KErrArgument; + const TSoInetIfQuery &info = (TSoInetIfQuery &)ref; + + // Search interface by index and hand some special options. + // [These are not really very natural for KSolInetIfQuery + // and probably should be placed under some other level] + CIp6Interface *const iface = FindInterface(info.iIndex); + if (iface == NULL) + return KErrNotFound; + switch (aName) + { + // Copy the zone id vector from option to the interface. + case KSoInetIfQuerySetScope: + { + TUint loopCount = sizeof(iface->iScope) / sizeof(iface->iScope[0]); + for (TUint i = 1; i < loopCount; ++i) + iface->iScope[i] = info.iZone[i]; + iface->NotifyInterfaceEvent(EventTypeModify); + return KErrNone; + } + // Clear "IS ROUTER" flag on interface (IPv6 Neighbour Advertisement IS_ROUTER flag) + case KSoInetIfQuerySetHost: + iface->iIsRouter = 0; + return KErrNone; + // Set "IS ROUTER" flag on interface (IPv6 Neighbour Advertisement IS_ROUTER flag) + case KSoInetIfQuerySetRouter: + iface->iIsRouter = 1; + return KErrNone; + default: + return KErrNotSupported; + } + } + else if (aLevel == KSolInetRtCtrl) + { + const TInt ret = aChecker.CheckPolicy(KPolicyNetworkControl, 0); + if (ret != KErrNone) + return ret; + if (aOption.Length() != sizeof(TSoInetRouteInfo)) + return KErrArgument; + + const TSoInetRouteInfo& opt = (TSoInetRouteInfo &)ref; + TInetAddr gateway(opt.iGateway); + if (gateway.Family() == KAfInet) + gateway.ConvertToV4Mapped(); + + // Because the iType of the TSoInetRouteInfo does not match very well + // with the internal route types, some "guesswork" is required to decide + // which type of route operated: + // + // The KSoInetRtCtrl supports the following: + // 1) Neighbor Cache entry, if iType == ERtIcmpAdd (prefix should be 128 bits!), + // 2) Gateway route, if iGateway was an IPv4 or IPv6 address (0 <= prefix <= 128) + // 3) Otherwise, Onlink route (0 <= prefix <= 128) + // + // For neighbor cache entry, the gateway address is the link layer address. + // + TUint rtype = + opt.iType == ERtIcmpAdd ? KRouteAdd_NEIGHBOR : + gateway.Family() == KAfInet6 ? KRouteAdd_GATEWAY : KRouteAdd_ONLINK; + + // + // Is interface specified by iIfAddr or iGateway? + // + CIp6Interface *iface = NULL; + if (opt.iIfAddr.Family() != KAFUnspec) + { + // + // Select interface by iIfAddr (IPv4 or IPv6 address) + // + iface = FindInterface(opt.iIfAddr); + } + else if (rtype == KRouteAdd_GATEWAY) + { + // Select interface by iGateway address + const CIp6Route *const rt = FindRoute(gateway.Ip6Address(), gateway.Scope(), + (TUint)(gateway.Ip6Address().Scope()-1)); + if (rt != NULL) + iface = &rt->iInterface; + } + // Must have interface... + if (iface == NULL) + return KErrNotFound; + + // + // Convert iDstAddr/iNetMask into prefix + // + TIp6Addr prefix_addr; + const TInt prefix_len = MagicGetAddress(prefix_addr, opt.iDstAddr, opt.iNetMask); + if (prefix_len < 0 || prefix_len > 128) + return KErrArgument; // Add ROUTE makes no sense without a valid prefix info! + + switch (aName) + { + case KSoInetDeleteRoute: + rtype |= KRouteAdd_UPDATEONLY; + break; + case KSoInetChangeRoute: + rtype |= KRouteAdd_UPDATEONLY; + /* FALLTRHOUGH */ + case KSoInetAddRoute: + // If this is adding/changing a gateway route, add also IS ROUTER status for the gateway! + if ((rtype & KRouteAdd_TYPEMASK) == KRouteAdd_GATEWAY) + { + TIp6Addr src; + CIp6Route *const n = iface->GetRoute(gateway.Ip6Address(), 128, KRouteAdd_ISROUTER); + if (n && n->iState == CIp6Route::EIncomplete) + if (iface->SelectSource(src,n->iPrefix)!=NULL) + n->StartND(src); + else + n->StartND(n->iInterface.iAddress.iId); + } + break; + default: + return KErrNotSupported; + } + + CIp6Route *const route = iface->GetRoute(prefix_addr, prefix_len, rtype, &gateway); + if (route == NULL) + return (rtype & KRouteAdd_UPDATEONLY) != 0 ? KErrNotFound : KErrNoMemory; + + if (aName == STATIC_CAST(TUint,KSoInetDeleteRoute)) + iface->RemoveRoute(route); + else + route->iMetric = opt.iMetric; + + ScanHoldings(); + return KErrNone; + } + return KErrNotSupported; + } + + +TInt CIp6Manager::SetIpv4LinkLocalOption(const TSoInetIpv4LinkLocalInfo &aOption) + { + CIp6Interface *iface = FindInterface(aOption.iInterface); + if (iface == NULL) + return KErrNotFound; + + return iface->SetIpv4LinkLocal(aOption.iFlag); + } + + +TInt CIp6Interface::SetIpv4LinkLocal(TUint aFlag) + /** + * Sets the IPv4 link-local flag for this interface. + * Possible parameter values are enumerated in EV4LLEnums. + * + * If the LL flag is set to disabled when link local addresses are used, + * the link-local prefix/id entries are set to deprecated state (without timeout) + * and they are left into the routing table. + */ + { + // Unlike the ini parameter, the socket option has only two valid choices: + // unconditional disable and unconditional enable + if (aFlag > 1) + return KErrArgument; + + iIpv4Linklocal = aFlag; + + for (;;) // FAKE LOOP, JUST FOR BREAK EXITS! + { + if (aFlag == EV4LLAlways) + { + // ConfigureLinkLocal() does not get confused even if it was called already + // earlier, so this should be safe operation + if (ConfigureLinkLocal(0) == 0) + break; // -- no change! + } + else if (aFlag == EV4LLDisabled) + { + // The code from this point on is only for deprecating IPv4 LL addresses when + // the user requests to disable LL when they were earlier enabled + + TIp6AddressInfo *const address = FindInternalIpv4LinkLocalAddr(); + if (address == NULL) + break; // -- no change (no LL address present) + + // Found IPv4 Link-local address (Id). Mark it as deprecated. + // The corresponding prefix will also get deprecated in the Timeout() + // function + address->iPLT = 0; + // Further processing for deprecation takes place in timeout handler + } + else + break; // -- no change + + // Address configuration has been changed, the process completes + // through the Timeout function, activate it. + TTime stamp; + stamp.UniversalTime(); + Timeout(stamp); + break; // ** ALWAYS EXIT THE FAKE LOOP + } + return KErrNone; + } + + +// CIp6Interface::FindIpv4LinkLocalAddr +// **************************************** +/** +// Find the one and only internally generated IPv4 link-local, if present. +// +// @return the TIp6AddressInfo, if such address exists; and NULL otherwise. +*/ +const TIp6AddressInfo* CIp6Interface::FindIpv4LinkLocalAddr() const + { + for (const TIp6AddressInfo *address = &iAddress;;) + { + if( address->IsSet() && address->iIpv4LinkLocal ) + { + // Caller does not get ownership of object. + return (TIp6AddressInfo *)address; + } + + if (address->iNext == NULL) + break; + address = &address->iNext->iInfo; + } + return NULL; + } + + +// +// CIp6Interface::NotifyFlows +// ************************** +// The interface has changed the state, notify the flows about this +// +void CIp6Interface::NotifyFlows(TInt aState, TBool aForce) const + { + // Interface state has changed. Notify all affected flows + // which have requested to be notified. + // Cannot just change the flow into ready, because the + // hooks may have something to add also. + // (Call RefreshFlows()? Ugh!!.. --msa) + TFlowNotifyList list; + for (CIp6Route *rt = iRouteList; rt; rt = rt->iNext) + for (CIp6Flow *f = rt->iFlowList; f; f = f->iNext) + { + // Temp. kludge -- msa + if (f->iPathMtu == 0 || (TInt)f->iPathMtu > iPMtu) + f->iPathMtu = iPMtu; // Minor kludge, fixes the + // problem when interface + // Mtu changes the Path Mtu + // -- msa + + // iIgnoreFlowContorol is set for flows that should + // not enter automaticly READY/HOLD state by a signal + // from the interface. A separate (external) module + // makes the decision for such flows... + // + if (aState < 0 || !f->iIgnoreFlowControl || aForce) + f->Notify(list, aState); + } + list.Deliver(aState); + } + + +// +// CIp6Interface::NotifyFlowsPmtu +// ****************************** +// Special method to define/change the Path MTU of the +// currently attached flows. +// +// The provider is not notified... should it? -- msa +// +void CIp6Interface::NotifyFlowsPmtu(const TUint aPmtu) const + { + for (const CIp6Route *rt = iRouteList; rt; rt = rt->iNext) + for (CIp6Flow *f = rt->iFlowList; f; f = f->iNext) + if (f->iPathMtu == 0 || f->iPathMtu > aPmtu) + f->iPathMtu = aPmtu; + } + + +// CIp6Interface::SetChanged +// ************************* +// Set iChanged for all flows on this interface +// +TInt CIp6Interface::SetChanged(const TInt aScope) const + { + if (aScope > 0) + return Interfacer().SetChanged(); + TInt count = 0; + for (CIp6Route *rt = iRouteList; rt; rt = rt->iNext) + count += rt->SetChanged(0); + return count; + } + +// +// CIp6Manager::SetChanged +// *********************** +// Set iChanged for all flows +// +TInt CIp6Manager::SetChanged() const + { + TInt count = 0; + for (CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + count += iface->SetChanged(0); + return count; + } + +// +// CIp6Manager::FindInterface +// ************************** +/** +// Based on CNifIfBase pointer, locate the internal +// CIp6Interface description that is connected to the +// this interface. Returns NULL, if none found. +// +// WARNING: (conserns both FindInterface methods) +// The search key is a pointer to memory block representing some +// structure and the point of these lookups is to guarantee that +// this value is still valid. However, there is a potential +// problem if the object being searched gets released and another +// object of the same type gets created using the same memory +// address. In such case FindInterface may return wrong interface! +// -- msa [needs to be checked whether this is a problem!] +*/ +CIp6Interface *CIp6Manager::FindInterface(const CNifIfBase *aInterface) const + { + if (aInterface == NULL) + return NULL; // Don't search for NULL! + for (CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + if (iface->iNifIf == aInterface) + return iface; + return NULL; + } +// +// CIp6Manager::FindInterface +/** +// This is mainly to safely convert aId parameter from MNifIfuser +// upcall into valid CIp6Interface pointer. (Just makes sure that +// the instance referred by aId really exists. +*/ +CIp6Interface *CIp6Manager::FindInterface(const TAny *aId) const + { + for (CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + if (iface == aId) + return iface; + return NULL; + } +// +// CIp6Manager::FindInterface +// Locate interface by address +CIp6Interface *CIp6Manager::FindInterface(const TInetAddr &aAddr) const + { + const TIp46Addr addr(aAddr); + const TUint32 scope_type = addr.Scope() - 1; + if (scope_type > EScopeType_NET) + return NULL; + const TUint32 scope_id = aAddr.Scope(); + for (CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + { + const TUint32 if_scope = iface->iScope[scope_type]; + if ((if_scope == 0 || scope_id == 0 || if_scope == scope_id) && iface->IsMyAddress(addr)) + return iface; + } + return NULL; + } + +// +// CIp6Manager::FindInterface +// Locate interface by Interface index +CIp6Interface *CIp6Manager::FindInterface(const TUint32 aIndex) const + { + return FindInterface(aIndex, EScopeType_IF); + } + +// CIp6Manager::FindInterface +// Locate Interface by a specific scope id. +// Note, that there can be multiple interfaces which match +// the condition. Only the first located is returned. +CIp6Interface *CIp6Manager::FindInterface(const TUint32 aIndex, const TScopeType aLevel) const + { + for (CIp6Interface *iface = iInterfaceList; iface != NULL; iface = iface->iNext) + if (iface->iScope[aLevel] == aIndex) + return iface; + return NULL; + } + +// CIp6Manager::FindInterface +// Locate Interface mathing the specified name +CIp6Interface *CIp6Manager::FindInterface(const TDesC &aName) const + { + for (CIp6Interface *iface = iInterfaceList; iface; iface = iface->iNext) + if (aName.Compare(iface->iName) == 0) + return iface; + return NULL; + } + + + +// CIp6Manager::Interface +// ********************** +// +const MInterface* CIp6Manager::Interface(const CNifIfBase *const aIf) const + { + return FindInterface(aIf); + } + +const MInterface* CIp6Manager::Interface(const TDesC &aName) const + { + return FindInterface(aName); + } + +const MInterface* CIp6Manager::Interface(const TUint32 aInterfaceIndex) const + { + return FindInterface(aInterfaceIndex); + } + +// +// CIp6Manager::StartSending +// ************************* +// +TInt CIp6Manager::StartSending(CNifIfBase *aIface) + { + if (aIface) + { + CIp6Interface* iface = FindInterface((CNifIfBase*)aIface); + if (iface) + { + LOG(Log::Printf(_L("\tIF %u [%S] StartSending"), iface->iScope[0], &iface->iName)); + const TInt transition = iface->StartSending(); + // + // StartSending from an interface implies that it is + // in READY state (can accept Send()). However this + // does not directly mean that flows can be opened + // yet. Update may have decided that information is + // still missing (prefixes, return PENDING) or detected + // some configuration error (return < 0). Other than + // pending, should be notified to flows. + // + LOG(Log::Printf(_L("\tIF %u [%S] StartSending, transition=%d"), iface->iScope[0], &iface->iName, transition)); + if (transition != KIfaceTransition_NONE) + { + iface->NotifyFlows(transition > 0 ? EFlow_READY : transition); + // if transition is UP, try waking up the holding flows + // (try to re-attach pending flows, in case this new interface fits + // some of them...) + if (transition == KIfaceTransition_UP) + ScanHoldings(); + } + return transition; + } + else + return KIfaceTransition_DOWN; + } + else + return KIfaceTransition_NONE; + } +// +// CIp6Manager::Error +// ****************** +// +// Comment/msa: It is somewhat unclear what it means when an interface +// calls Error() of the network layer. What is the expected effect? +// +// a) just report it to flows, but leave interface into OK state? +// b) report to flows, put interface into error state until +// either StartSending() clears it, or interface goes down? +// +// The current implementaion does (b). +// +TInt CIp6Manager::Error(TInt aError, CNifIfBase *aIface) + { + if (aIface) + { + CIp6Interface* iface = FindInterface((CNifIfBase*)aIface); + if (iface) + { + LOG(Log::Printf(_L("CIp6Manager::Error(%d, %S)"), aError, &iface->iName)); + + if (iface->iState >= EFlow_READY) + iface->iState = aError; + // + // Notify flows that want interface errors... + // + if (aError < 0) + { + iface->NotifyFlows(aError); + // + // For the remaining flows, interface going down is not a fatal + // error. Move all attached flow to the holding route (pending state). + // + // Note: if there is no holding route, flows will be terminated + // by the Reset(). + for (CIp6Route *rt = iface->iRouteList; rt != NULL; rt = rt->iNext) + MoveToHolding(*rt); + iface->Reset(1); // Reset to initial state, but keep binding to NIF + } + } + // + // This is somewhat kludgy. Allow Error to used by the interface to set the + // interface state into non-error state (like pending). If such call happens, + // return with NONE transition to prevent unnecessary further processing. + return aError < 0 ? aError : KIfaceTransition_NONE; + } + else + return KIfaceTransition_NONE; + } + + +// +// CIp6Manager::IcmpHandler +// ************************ +/** +// Gets a peek at all received non-error ICMP's +// +// @return +// @li < 0, if packet has been released (packet will not +// go to the upper layer after this), +// @li = 0, the usual return, packet looked and it can be +// passed to the upper layers +// @li > 0, *NOT USED NOW*, Treat as = 0 as default +*/ +TInt CIp6Manager::IcmpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo) + { + if (aInfo.iProtocol != STATIC_CAST(TInt,KProtocolInet6Icmp)) + return 0; // For now, only ICMP6 is interesting! + // + // For validity checking, the hoplimit/TTL is required.. get it.. + // + const TIpHeader *const ip = ((RMBufPacketPeek &)aPacket).GetIpHeader(); + if (!ip) + return 0; // should probably drop the packet + if (255 != ((aInfo.iVersion == 4) ? ip->ip4.Ttl() : ip->ip6.HopLimit())) + return 0; // All ND ICMP's must have hoplimit == 255! + + TInet6Packet nd(aPacket, aInfo.iOffset); + + if (nd.iHdr == NULL || + nd.iHdr->iIcmp.Code() != 0) + return 0; // Not for me! + + CIp6Interface *const srcif = FindInterface(aInfo.iInterfaceIndex); + if (!srcif) + return 0; + return srcif->IcmpHandler(aPacket, aInfo, nd); + } + + +// +// CIp6Interface::IcmpHandler +// ************************** +/** +// The CIp6Manager::IcmpHandler determined that the basic ICMP is +// valid (for ND), and belongs to the current interface, where +// the actual ICMP processing occurs. +// +// @return +// @li < 0, if packet has been released (packet will not +// go to the upper layer after this), +// @li = 0, the usual return, packet looked and it can be +// passed to the upper layers +// @li > 0, *NOT USED NOW*, Treat as = 0 as default +*/ +TInt CIp6Interface::IcmpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, TInet6Packet &aNd) + { +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::IcmpHandler()"))); + #endif +#endif // SYMBIAN_TCPIPDHCP_UPDATE + TInt notify = 0; +#ifndef SYMBIAN_TCPIPDHCP_UPDATE + TInt dns_flag = 0; +#endif //SYMBIAN_TCPIPDHCP_UPDATE + TInt count, start; + + const TInt icmp_type = aNd.iHdr->iIcmp.Type(); + TLinkAddr source_link, target_link; + + const TIp6Addr &icmp_src_addr = TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address(); + const TIp6Addr &icmp_dst_addr = TInetAddr::Cast(aInfo.iDstAddr).Ip6Address(); + +#ifdef _LOG + // src & dst for logging purposes + TBuf<70> tmpsrc, tmpdst; + TInetAddr::Cast(aInfo.iSrcAddr).OutputWithScope(tmpsrc); + TInetAddr::Cast(aInfo.iDstAddr).OutputWithScope(tmpdst); +#endif + + + // Setup and Check Validity (part of it) + // ************************************* + // + // The ICMP source address can only be either a valid unicast address + // or unspecified address in some cases for RS and NS. + // + const TInt icmp_src_unspecified = icmp_src_addr.IsUnspecified(); + if (TIp46Addr::Cast(icmp_src_addr).IsMulticast()) + goto drop_packet; // Was not unicast or unspecified + + switch (icmp_type) + { + case KInet6ICMP_RouterSol: + LOG(Log::Printf(_L("\tIF %u [%S] Received RS src=[%S] dst=[%S]"), iScope[0], &iName, &tmpsrc, &tmpdst)); + start = aNd.iHdr->iRS.HeaderLength(); + break; + case KInet6ICMP_RouterAdv: + // + // A Router Advertisement ICMP detected + // + LOG(Log::Printf(_L("\tIF %u [%S] Received RA src=[%S] dst=[%S]"), iScope[0], &iName, &tmpsrc, &tmpdst)); + if (!icmp_src_addr.IsLinkLocal()) // .. src must be a link local address. + goto drop_packet; + start = aNd.iHdr->iRA.HeaderLength(); + break; + case KInet6ICMP_NeighborSol: + LOG(Log::Printf(_L("\tIF %u [%S] Received NS src=[%S] dst=[%S]"), iScope[0], &iName, &tmpsrc, &tmpdst)); + start = aNd.iHdr->iNS.HeaderLength(); + // RFC-2461 says to require !Multicast.. but, that would let Unspecified through! + // Is it an error in RFC or not? (Need to check message length here, becuase of + // accessing of the target field!) + if (start > aNd.iLength || !aNd.iHdr->iNS.Target().IsUnicast()) + goto drop_packet; + // The NS destination address is either the target address or.. + if (!icmp_dst_addr.IsEqual(aNd.iHdr->iNS.Target())) + { + // ..it must be the solicited node multicast corresponding + // the target address (RFC-2461, 4.3) + TSolicitedNodeAddr solicited(aNd.iHdr->iNS.Target()); + if (!icmp_dst_addr.IsEqual(solicited)) + goto drop_packet; + } + else if (icmp_src_unspecified) + // ..apparently, src=:: && target==dst is invalid combination + // (TAHI), and packet must be dropped... + goto drop_packet; + break; + case KInet6ICMP_NeighborAdv: + LOG(Log::Printf(_L("\tIF %u [%S] Received NA src=[%S] dst=[%S]"), iScope[0], &iName, &tmpsrc, &tmpdst)); + start = aNd.iHdr->iNA.HeaderLength(); + // RFC-2461 says to require target != Multicast.. but, that would let Unspecified through! + // Is it an error in RFC or not? (Need to check message length here, becuase of + // accessing of the target field!) + if (icmp_src_unspecified || start > aNd.iLength || !aNd.iHdr->iNA.Target().IsUnicast()) + goto drop_packet; + // Note: Solicited bit/Target Link-layer option vs. multicast destination + // check is performed after the options pass (see there.) [Can do this, + // because *currently* none of the NA option processing "commits" any + // changes to the system state.. however, watch it -- msa] + break; + case KInet6ICMP_Redirect: + LOG(Log::Printf(_L("\tIF %u [%S] Received Redirect src=[%S] dst=[%S]"), iScope[0], &iName, &tmpsrc, &tmpdst)); + if (!icmp_src_addr.IsLinkLocal()) // .. src must be a link local address. + goto drop_packet; + start = aNd.iHdr->iRD.HeaderLength(); + if (start > aNd.iLength || !aNd.iHdr->iRD.Destination().IsUnicast()) + goto drop_packet; + break; + default: + return 0; // Not for me + } + // + // Process Options + // *************** + // (to be 100% right in everything, one should probably make + // this section a separate method with two operating modes, and + // which is called twice in processing the ND ICMP: (1) to check + // validity of everything, and if all is OK, (2) execute the + // options. -- msa) + // + // Note: *Currently* only RA's may run into this problem, because + // RA is the only ND ICMP, in which the options are actually + // "committed" directly in the options processing (Prefix, Mtu). + // + start += aInfo.iOffset; + count = aInfo.iLength - start; + if (count < 0) + goto drop_packet; // Message is too short to be valid ND. + while (count > 0) + { + TIcmpNdOption option; + TPtr8 opt((TUint8 *)&option, sizeof(option), sizeof(option)); + + RMBuf *p; + TInt offset, len; + TUint8 *ptr, type; + + if (!aPacket.Goto(start, p, offset, len)) + return 0; // Drop instead? + ptr = p->Buffer() + offset; + type = *ptr++; + --len; + while (len == 0) // Should loop only once, but 'while' just in + // case someone wants RMBuf with zero length... + { + p = p->Next(); + if (p == NULL) + return 0; + len = p->Length(); + ptr = p->Ptr(); + } + len = *ptr; + // + // All included options must have length > 0. However, by coding + // it this way, there is a problem that all preceding valid options + // have already been executed and system state may have been changed + // for those. To fully comply, one should do two passes over the + // options, first to check the validity and then execute. -- msa + if (len < 1) + goto drop_packet; // Option length < 1, drop packet! + len <<= 3; + // We don't want panic from perfectly legal but unknown to us + // options that are longer than any of the "known" ones. Thus, + // constrain SetLength()... + opt.SetLength(len > opt.MaxLength() ? opt.MaxLength() : len); + aPacket.CopyOut(opt, start); + switch (type) + { + // source and target link options are only accepted, if the + // link layer supports addresses, and ignored otherwise. + + case KInet6OptionICMP_SourceLink: // Source link-layer address + if (icmp_src_unspecified) + goto drop_packet; // Illegal, if unspecified source! + if (iHwAddr.Family() != KAFUnspec) + { + source_link.SetAddress(option.iLink.Address()); + source_link.SetFamily(iHwAddr.Family()); + } + break; + case KInet6OptionICMP_TargetLink: // Target link-layer address + if (iHwAddr.Family() != KAFUnspec) + { + target_link.SetAddress(option.iLink.Address()); + target_link.SetFamily(iHwAddr.Family()); + } + break; + case KInet6OptionICMP_Mtu: // MTU + if (icmp_type == KInet6ICMP_RouterAdv) + SetMtu(option.iMtu.Mtu(), KInet6MinMtu); + break; + case KInet6OptionICMP_Prefix: // Prefix Information + if (icmp_type == KInet6ICMP_RouterAdv && !option.iPrefix.Prefix().IsLinkLocal()) + { + const TInt length = option.iPrefix.PrefixLength(); + const TLifetime lifetime = option.iPrefix.ValidLifetime(); + const TLifetime preferred = option.iPrefix.PreferredLifetime(); + + if (length > 128) + goto drop_packet; // Garbage! + if (preferred > lifetime) + break; // Ignore Option with illogical lifetimes + if (option.iPrefix.AFlag()) // Can use this for address generation? + // ...should set the aForce flag, if RA was protected by IPSEC... -- msa + { +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::IcmpHandler() option.iPrefix.AFlag()"))); + #endif + iGlobalflag = ETrue; +#endif //SYMBIAN_TCPIPDHCP_UPDATE + SetPrefix(option.iPrefix.Prefix(), length, !NeedsND(), lifetime, preferred); + } + // Note: now multicasts and unspecified addresses are accepted. + // Is this a "feature" or should it be prevented? -- msa + if (option.iPrefix.LFlag()) + (void)GetRoute(option.iPrefix.Prefix(), length, KRouteAdd_ONLINK, NULL, &lifetime); + notify++; // .. only for new prefixes. + } + break; +#ifdef SYMBIAN_TCPIPDHCP_UPDATE +//RFC 5006 Changes + case KInet6OptionICMP_RDNSS: + // Process RDNSS only if M Flag is set + if (aNd.iHdr->iRA.M()) + { + if(iRdnssList == NULL) + { + iRdnssList = CManageRdnssServerList::NewL(); + } + if( (aNd.iHdr->iRA.RouterLifetime())!= 0) + { + LOG(Log::Printf(_L("\tIF %u [%S] RECEIVED RDNSS OPTION"), iScope[0], &iName)); + TInet6OptionICMP_DnsInformationV1 rdnssOption = option.iDnsInformation; + + TUint8 numRdnssAddr; + if(iRdnssList->RdnssParseOptionHdr(rdnssOption,numRdnssAddr)) + { + //Update the received RDNSS entry into RDNSServerList + iRdnssList->RdnssProcessOptionData(rdnssOption, numRdnssAddr); + iRdnssList->RdnssNameServerUpdate(iNameSer1,(TUint8)0); + iRdnssList->RdnssNameServerUpdate(iNameSer2,(TUint8)1); + } + else + { + delete iRdnssList; + iRdnssList = NULL; + goto drop_packet; + } + } + else + // Router Lifetime is 0, Delete all RDNSS entries + { + iRdnssList->RdnssServerListDelete(); + // Reset Respository iNameServer1 and iNameServer2 to KAFUnspec + iRdnssList->RdnssNameServerReset(iNameSer1,iRdnssList->GetRdnssFlag()); + iRdnssList->RdnssNameServerReset(iNameSer2,iRdnssList->GetRdnssFlag()); + } + + } + break; +#endif // SYMBIAN_TCPIPDHCP_UPDATE + default: + // Handle options, for which is is no fixed assigned type by IANA, and which are + // configured via TCPIP.INI. Option is enabled, if a non-zero type code is configured. + // Because the 0 value has own case in switch, there is no need to test against + // ZERO here (unconfigured option type is ZERO and never matches). + if (icmp_type == KInet6ICMP_RouterAdv) + { + if (type == Interfacer().iRA_OptRoute) + { + // Route Information Option + // Experimental: draft-draves-ipngwg-router-selection-01.txt + // Default Router Preferences and More-Specific Routes + // + const TInt preference = option.iRouteInformation.Prf(); // range guaranteed to be [0..3]! + if (preference == ERoutePreference_INVALID) + break; // invalid preference value + if (option.iRouteInformation.PrefixLength() > 128) + goto drop_packet; // Garbage ! + const TLifetime lifetime = option.iRouteInformation.RouteLifetime(); + // Because the option is copied into a temporary space, Prefix() method is + // "safe" to use... (see comment on it's definition!). (However, might + // consider opt.FillZ before CopyOut to remove possible distracting, but + // harmless garbage. + CIp6Route *const route = GetRoute( + option.iRouteInformation.Prefix(), + option.iRouteInformation.PrefixLength(), + KRouteAdd_GATEWAY, + &aInfo.iSrcAddr, // The gateway address + &lifetime); + if (route) + route->iMetric = KPreferenceMetric[preference]; + } +#ifndef SYMBIAN_TCPIPDHCP_UPDATE + else if (type == Interfacer().iRA_OptDns) + { + // Experimental: draft-jeong-dnsop-ipv6-discovery-03.txt + // IPv6 DNS Configuration based on Router Advertisement + // + // *WARNING* Just a "proof of concept", not complete + // implementation (more like a "placeholder code", indicates + // the point where real implementation should go...) + // - preference is ignored + // - other than "delete", lifetime is ignored + // - only two first addresses processed + // - does not do much sanity check (delete and insert same address). + const TIp6Addr &addr = option.iDnsInformation.Address(); + const TLifetime lifetime = option.iDnsInformation.Lifetime(); + if (lifetime == 0) + { + // Should remove the matching DNS server address + // (if present) + if (iNameSer1.Ip6Address().IsEqual(addr)) + { + iNameSer1.Init(KAFUnspec); + dns_flag |= 4; // mark "dns changed" + } + if (iNameSer2.Ip6Address().IsEqual(addr)) + { + iNameSer2.Init(KAFUnspec); + dns_flag |= 4; // mark "dns changed" + } + } + else if (!addr.IsUnspecified()) + { + // Add DNS server address + // (ignore prefs, take the first two) + if ((dns_flag&1) == 0) + { + iNameSer1.SetAddress(addr); + dns_flag |= 1; + } + else if ((dns_flag&2) == 0) + { + iNameSer2.SetAddress(addr); + dns_flag |= 2; + } + } + } +#endif //SYMBIAN_TCPIPDHCP_UPDATE + } + break; + case 0: // To avoid getting into default branch with type==0! + break; + } + // + // Advance to the next option + // + count -= len; + start += len; + } + + // + // Execute the actual ND ICMP + // ************************** + // + TIp6AddressInfo *my_id; + + switch (icmp_type) + { + case KInet6ICMP_RouterAdv: + // + // The router is advertising as a default route + // Add a default route entry (will also handle changes + // in the lifetime and even destruction, if lifetime == 0. + // + // *NOTE* Even if the RouterLifeTime is 0, ISROUTER is set, because + // this is a valid router, it's just not a default router. + // + (void)GetRoute(icmp_src_addr, 128, KRouteAdd_OVERRIDE|KRouteAdd_ISROUTER, &source_link); + { + const TInt preference = aNd.iHdr->iRA.Prf(); // range guaranteed to be [0..3]! + // If prf is invalid, don't install default route! + const TLifetime lifetime = (preference == ERoutePreference_INVALID) ? 0 : aNd.iHdr->iRA.RouterLifetime(); + // Disable Router Discovery process (sending RS's), if + // at least one RS has been sent, see RFC 2461 6.3.7), + // And if this RA had non-zero lifetime. + if (lifetime && iRetryRS > 0) + iRetryRS = KMaxTUint8; // ...should be large enough! + CIp6Route *const route = GetRoute(KInet6AddrNone, 0, KRouteAdd_GATEWAY, &aInfo.iSrcAddr, &lifetime); + if (route) + route->iMetric = KPreferenceMetric[preference]; + } + notify++; + if (aNd.iHdr->iRA.ReachableTime() && aNd.iHdr->iRA.ReachableTime() != iND.iReachableTime) + { + iND.iReachableTime = aNd.iHdr->iRA.ReachableTime(); + SetReachableTime(); + } + if (aNd.iHdr->iRA.RetransTimer() && aNd.iHdr->iRA.RetransTimer() != iND.iRetransTimer) + { + iND.iRetransTimer = aNd.iHdr->iRA.RetransTimer(); + SetRetransTimer(); + } + if (aNd.iHdr->iRA.CurHopLimit()) + iHopLimit = aNd.iHdr->iRA.CurHopLimit(); + break; + case KInet6ICMP_NeighborSol: + { +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::IcmpHandler() KInet6ICMP_NeighborSol recieved NS"))); + #endif +#endif //SYMBIAN_TCPIPDHCP_UPDATE + // include proxy into "my address" class + TIp6Addr &target = aNd.iHdr->iNS.Target(); + my_id = IsMyAddress(target, 1); + if (my_id == NULL) + { + // Target is not my assigned address, ... + if (!icmp_src_unspecified) + break; + // ..., but the NS was a DAD probe. Check if + // the plain ID part matches with any of my id's (including tentative ones)... + // + // * REQUIRE ID IS USED ONLY BY ONE HOST * + // + my_id = IsMyId(target); + if (my_id == NULL) + break; // Target has no relation with me, ignore NS. + if (my_id->IsTentative()) + { + // + // Someone is doing Duplicate Address Detection on my tentative address! + // Decide this a duplicate address collision.. + // + RemId(my_id); // Kill this ID + notify++; + break; + } + // + // Someone is doing Duplicate Address Detection on a address with a prefix + // which is not used by me currently (because IsMyAddress() failed, but the + // ID part matches one of my assigned id's. [If I ever acquire the same prefix, + // there will be a collisions and confusion...]. + // + // Could try to kill it by sending DAD probe, but that would result immediate + // packet storm, if two hosts on the net had this strategy... -- msa + // Instead, fall through to the standard NA code (and "incorrectly" just advertise the + // address...) + if (Interfacer().iNoDefendId) + break; // "Defending ID" has been disabled, ignore DAD NS + } + // + // Normal/DAD NS for my assigned address + // + CIp6Route *route = NULL; + const TIp6Addr *dst = &KInet6AddrAllNodes; + if (!icmp_src_unspecified) + { + dst = &icmp_src_addr; + // We need the route entry, whether link layer is known or not + route = GetRoute(*dst, 128, KRouteAdd_OVERRIDE, &source_link); + if (!route) + break; // No route, and it couldn't be created for some reason! + // Things get awkward if there is no cached link layer address + // to be used in the reply. + if (route->iState == CIp6Route::EIncomplete) + route->StartND(my_id->iId);// Use my link local as ND source! + } +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + //If it is DADNS,the solicitation's Source Address is unspecified then the destination address for the NA should be + //the all-nodes multicast address(Refer RFC 4861 sec 4.4) + else if(icmp_src_unspecified) + { + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::IcmpHandler() KInet6ICMP_NeighborSol recieved NS (icmp_src_unspecified)"))); + #endif + //Get the target route entry + CIp6Route *route1 = GetRoute(aNd.iHdr->iNS.Target(), 64, KRouteAdd_MYPREFIX | KRouteAdd_UPDATEONLY); + if (route1==NULL) + { + //if the target route is expired/no route dont send NA + #ifdef _DEBUG + LOG(Log::Printf(_L("<>\tCIp6Interface::IcmpHandler() KInet6ICMP_NeighborSol recieved NS route1==NULL"))); + #endif + break; // No route, and it couldn't be created for some reason! + } + // Things get awkward if there is no cached address + // to be used in the reply. + if (route1->iState == CIp6Route::EIncomplete) + route1->StartND(my_id->iId);// Use my target address as ND source! + } +#endif // SYMBIAN_TCPIPDHCP_UPDATE + const TInt message_type = KInet6ICMP_NeighborAdv | (my_id->IsProxy() ? KSendNeighbors_NO_OVERRIDE : 0); +#ifdef _LOG + TInetAddr tmp(route ? route->iPrefix : KInet6AddrNone, 0); + tmp.OutputWithScope(tmpsrc); + tmp.SetAddress(aNd.iHdr->iNS.Target()); + tmp.OutputWithScope(tmpdst); + Log::Printf(_L("\tIF %u [%S] Sending NA(%d) dst=[%S] target=[%S]"), iScope[0], &iName, message_type, &tmpsrc, &tmpdst); +#endif + SendNeighbors(message_type, route, target); + } + break; + case KInet6ICMP_NeighborAdv: + { + LOG(Log::Printf(_L("<>\tCIp6Interface::IcmpHandler() KInet6ICMP_NeighborAdv recieved NA"))); + const TInt flags = KRouteAdd_NEIGHBOR | KRouteAdd_UPDATEONLY | + (aNd.iHdr->iNA.O() ? KRouteAdd_OVERRIDE : 0) | + (aNd.iHdr->iNA.S() ? KRouteAdd_SOLICITED : 0) | + (aNd.iHdr->iNA.R() ? KRouteAdd_ISROUTER : KRouteAdd_ISHOST); + // Additional "validity checks" for NA... + const TInt dst_is_mc = TIp46Addr::Cast(icmp_dst_addr).IsMulticast(); + if (flags & KRouteAdd_SOLICITED) + { + // If destination was multicast, solicited bit cannot be set. + if (dst_is_mc) + goto drop_packet; + } + TIp6Addr &target = aNd.iHdr->iNS.Target(); + my_id = IsMyId(aNd.iHdr->iNA.Target()); + if (my_id) + { + // Someone is advertising with id part matching my id??? + // + // * REQUIRE ID IS USED ONLY BY ONE HOST * + // + if (my_id->IsTentative()) + { + + // If doing Duplicate Address Detection, assume duplicate, if NA + // for my tentative address is received! (RFC-2462, 5.4.4) + RemId(my_id); // Kill this ID + notify++; + +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + //A tentative address that is determined to be a duplicate as described + //above MUST NOT be assigned to an interface, and the node SHOULD log a + //system management error (RFC-4862,5.4.5) + iState = EFlow_NOTCONFIGURE; + NotifyInterfaceEvent(EventTypeModify); + LOG(Log::Printf(_L("\t Interface is not configured as DAD detects Duplicate Adress"))); +#endif //SYMBIAN_TCPIPDHCP_UPDATE + break; + } + // + // Here is a problem: Someone is using address that has the same id part + // as I have... + if (IsMyAddress(target, 1)) + { + // ..it's even my current address. Just ignore it (but, there is + // another host on link using my address--this is not a good + // situation!) + break; + } + // The address has my id, but with a prefix which not configured (YET!) + // for me. Accept advertisement. This works fine as long as the prefix + // does not get autoconfigured for me (e.g. as long as no router + // advertises it with A=1 in prefixes). + } + if (dst_is_mc && iHwAddr.Family() != target_link.Family()) + // Multicast NA *MUST* have target link, if the link + // is using addresses. (however, allow them to affect + // DAD process and test this after that) + goto drop_packet; + // Must not create entry, if it does not already exist + (void)GetRoute(target, 128, flags, &target_link); + } + break; + case KInet6ICMP_Redirect: + { + // More validity checks: accept redirects only if they actually come from a router that + // would be getting the packets sent to the specified Destination address. +#ifdef _LOG + // reuse tmpsrc for target + // reuse tmpdst for destination + TInetAddr tmp; + tmp.SetAddress(aNd.iHdr->iRD.Destination()); + tmp.OutputWithScope(tmpdst); + tmp.SetAddress(aNd.iHdr->iRD.Target()); + tmp.OutputWithScope(tmpsrc); +#endif + // Need to beef up the route with scope? + const TUint dstType = (TUint)(aNd.iHdr->iRD.Destination().Scope()-1); + const CIp6Route *const route = + dstType > EScopeType_NET ? NULL : Interfacer().FindRoute(aNd.iHdr->iRD.Destination(), iScope[dstType], dstType); + + if (route != NULL && + &route->iInterface == this && + route->IsGateway() && + icmp_src_addr.IsEqual(route->iAddress.Ip6Address())) + { + LOG(Log::Printf(_L("\tIF %u [%S] Redirect [%S] to [%S] accepted"), iScope[0], &iName, &tmpdst, &tmpsrc)); + TInt flags = KRouteAdd_OVERRIDE; + TIp6Addr &target = aNd.iHdr->iRD.Target(); + if (!aNd.iHdr->iRD.Destination().IsEqual(target)) + { + if (aNd.iHdr->iRD.Target().IsLinkLocal()) + flags |= KRouteAdd_ISROUTER; + else + goto drop_packet; + } + // Create host route for the target + (void)GetRoute(aNd.iHdr->iRD.Target(), 128, flags, &target_link); + if (flags & KRouteAdd_ISROUTER) + { + // Target is a router, need to add redirect route for the destination, + // pointing to the gateway (= target). + TInetAddr gateway; + gateway.SetAddress(target); + (void)GetRoute(aNd.iHdr->iRD.Destination(), 128, CIp6Route::ERedirect, &gateway); + } + // + // Previously, any flow using the rerouted destination address was assigned + // to the 'route'. Need to kick those flows to recheck their nexthop (e.g. + // all flows going to the 'destination' must now be assigned to the new + // redirected route + // Note: this may be bad thing, if there are many redirects and many unaffected + // flows are attached to the 'route' (which could be the default route). + // - more intelligent SetChanged(new_route), wich only affect the flows that have + // a better match with the new route (the redirect), or + // - change of logic and implement true "destination cache" (flow send would have + // to look into it for every packet) [or, view flows themselves as "destination + // cache"?] + // -- msa + route->SetChanged(); + } + else + { + LOG(Log::Printf(_L("\tIF %u [%S] Redirect [%S] to [%S] rejected"), iScope[0], &iName, &tmpdst, &tmpsrc)); + goto drop_packet; + } + } + break; + default: + break; + } + // + // + if (notify) + { + NotifyFlows(EFlow_READY); + Interfacer().ScanHoldings(); + } + +#ifdef SYMBIAN_TCPIPDHCP_UPDATE + //RFC-5006 Changes + if (iRdnssList!=NULL) + { + if(iRdnssList->GetRdnssFlag()) + { + // DNS information changed + NotifyInterfaceEvent(EventTypeModify); + } + } +#else + if (dns_flag) + { + // DNS information changed + NotifyInterfaceEvent(EventTypeModify); + } +#endif // SYMBIAN_TCPIPDHCP_UPDATE + + return 0; + // + // Drop packets (should only be used for obviously bad packets) + // +drop_packet: + aPacket.Free(); + return -1; + } + + +#ifdef ARP +// +// CIp6Manager::ArpHandler +// *********************** +/** +// Receives all ARP packets +// +// Currently always "consumes" packet, +// and return is always < 0 +*/ +TInt CIp6Manager::ArpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo) + { + TInet6Packet arp(aPacket); + for (;;) + { + if (arp.iHdr == NULL) + break; // Too short, ignore! + // Remap with real packet length, and assume all of the + // ARP packet will fit into single RMBuf. Should be okay, + // as only IPv4 is being implemented and map will only + // fail if hwaddr length is > 56 bytes! Computed by + // RMBuf = 128 bytes, prlen = 4 for IPv4, fixed part is + // 8 bytes => (128 - 8 - 2*prlen) / 2 = 56. For longer + // than 56 hwaddr, this ARP stops working! -- msa + arp.Set(aPacket, 0, arp.iHdr->HeaderLength()); + if (arp.iHdr == NULL) + break; // Too short (corrupt packet, or hwlen > 56) + if (arp.iHdr->PrAddrLen() != 4 || + arp.iHdr->ProtocolType() != KArpProtocolType_IP) + break; // Only IPv4 supported. + CIp6Interface *const srcif = FindInterface(aInfo.iInterfaceIndex); + if (!srcif) + break; // Cannot find interface... + (void)srcif->ArpHandler(aPacket, aInfo, arp); + break; // Alway exit the loop! + } + // + // Packet always consumed, whether processed or not! + // + aPacket.Free(); + if (iScanHolding) + ScanHoldings(); + return -1; + } + +TInt CIp6Interface::ArpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, TInet6Packet &aArp) + { + if (iHwAddr.GetUserLen() != aArp.iHdr->HwAddrLen()) + return 0; // Hardware address length does not match the interface, ignore ARP! + if (aArp.iHdr->PrAddrLen() != 4) + return 0; // Protocol address is IPv4, must be 4 bytes long! + const TUint operation = aArp.iHdr->Operation(); + if (operation != EArpOperation_REQUEST && operation != EArpOperation_REPLY) + return 0; // Only the basic REQUEST/REPLY are supported! + + TIp46Addr sender(0), target(0); + TPtr8(&sender.u.iAddr8[12], 4).Copy(aArp.iHdr->SenderPrAddr()); + TPtr8(&target.u.iAddr8[12], 4).Copy(aArp.iHdr->TargetPrAddr()); + + TLinkAddr sender_link, target_link; + sender_link.SetFamily(iHwAddr.Family()); + target_link.SetFamily(iHwAddr.Family()); + sender_link.SetAddress(aArp.iHdr->SenderHwAddr()); + target_link.SetAddress(aArp.iHdr->TargetHwAddr()); +#ifdef _LOG + { + TLogAddressPrefix link(TInetAddr::Cast(sender_link)); + TLogAddressPrefix ip(sender); + Log::Printf(_L("\tIF %u [%S] ARP (bytes=%d) sender=%S [%S]"), iScope[0], &iName, aInfo.iLength, &ip, &link); + link.Set(target_link); + ip.Set(target); + Log::Printf(_L("\t\tTarget=%S [%S]"), &ip, &link); + } +#endif + if (sender_link.Address() == iHwAddr.Address()) + { + // The sender has my hardware address. This may happen because + // 1) this is my own ARP echoed back from the link for some reason + // 2) someone else on the link has the same link layer address + // In either case, there is not much that can be done, just drop + // the ARP for now... + LOG(Log::Printf(_L("\t\tARP sender link is my hwaddr, ARP ignored"))); + return 0; + } + // + // Assume IPv4 addressess are stored as 128 bit ids (e.g. iPrefix == 0) + // (thus it is sufficient to check the id) + // + TIp6AddressInfo *my_id = IsMyId(target); + if (my_id && my_id->iPrefix != 0) + my_id = NULL; // ignore other matches + + // DAD stuff + // ********* + // ..if sender matches tentative => declare collision on sender_id + // ..if sender matches my address => declare nasty collision on sender_id + // ..if sender is 0.0.0.0 and target matches tentative => declare collision on my_id + // + TIp6AddressInfo *dup_id = IsMyId(sender); + if (dup_id && dup_id->iPrefix != 0) + dup_id = NULL; + + if (my_id && my_id->IsTentative()) + { + if (sender.u.iAddr32[3] == 0) + { + // If sender = None, it couldn't have been found as duplicate! + ASSERT(dup_id == NULL); + dup_id = my_id; + } + my_id = NULL; // Anyways, it's not yet my official address, do not reply! + } + TBool gratuitousArpFromOtherHost = EFalse; + TBool defendIPAddress = EFalse; + if (dup_id) + { + if (aArp.iHdr->SenderHwAddr() != iHwAddr.Address() && operation == EArpOperation_REQUEST) + gratuitousArpFromOtherHost = ETrue; + DuplicateAddress(dup_id, defendIPAddress, gratuitousArpFromOtherHost); + if(!defendIPAddress) + return 0; + } + + if(!defendIPAddress) + { + // + // Choose Flags, if target==me, force creation of the entry + // + const TInt flags = KRouteAdd_OVERRIDE | + ((my_id != NULL) ? + (operation == EArpOperation_REPLY ? KRouteAdd_SOLICITED : 0) : + KRouteAdd_UPDATEONLY); + + // Update neighbor cache only if sender IP address is defined + if (sender.u.iAddr32[3]) + (void)GetRoute(sender, 128, flags, &sender_link); + } + + // If target was me and operation is a Request, swap sender and target + // and fill in my hw address. + if ((my_id || defendIPAddress) && operation == EArpOperation_REQUEST) + { + if(defendIPAddress) + { + aArp.iHdr->TargetHwAddr().Copy(iHwAddr.Address()); + } + else + { + aArp.iHdr->TargetHwAddr().Copy(aArp.iHdr->SenderHwAddr()); + } + aArp.iHdr->TargetPrAddr().Copy(aArp.iHdr->SenderPrAddr()); + aArp.iHdr->SenderHwAddr().Copy(iHwAddr.Address()); + aArp.iHdr->SenderPrAddr().Copy(TPtrC8(&target.u.iAddr8[12], 4)); + aArp.iHdr->SetOperation(EArpOperation_REPLY); + aInfo.iProtocol = KProtocolArp; + + // draft-ietf-zeroconf-ipv4-linklocal-05.txt says that whenever + // the sender is ipv4 link local, then the replies (and requests) + // must always be sent to the broadcast address [IMHO, this is + // a bit dubious rule, but if it is so specified, comply... -- msa] + if (target.Scope() == KIp6AddrScopeLinkLocal) + { + TInetAddr::Cast(aInfo.iDstAddr).SetAddress(KInetAddrBroadcast); + aInfo.iFlags = KIpBroadcastOnLink; // Dst is broadcast. + } + else + { + aInfo.iDstAddr = sender_link; + aInfo.iFlags = 0; // Dst is unicast hw address. + } + if (iNifIf) + { + aPacket.Pack(); + iNifIf->Send(aPacket, NULL); + } + } + return 0; + } + +#endif + + +// CIp6Interface::Ip4RedirectHandler +// ********************************* +/** +// Handle IPv4 Redirect ICMP +// +// @param aPacket the returned ICMP error packet +// @param aInfo the associated info (iIcmp != 0) +*/ +void CIp6Interface::Ip4RedirectHandler(const RMBufRecvPacket &aPacket, const RMBufRecvInfo &aInfo) + { + // Because IPv4 ICMP redirect is catched from the ICMP Error handling + // path, the info block is already loaded with the data extracted from + // from ICMP error message as follows: + // + // - aInfo.iParemeter == Gateway address (IPv4 in host byteorder) + // - aInfo.iSrcAddr == should be my address of the original packet + // - aInfo.iDstAddr == should be the destination of the original packet + // + // Handle all redirects (codes 0-3) as host redirects (ignore other codes) + if (aInfo.iCode > 3) + return; + + const TIp46Addr gateway(aInfo.iParameter); + const TIp6Addr &dst_addr = TInetAddr::Cast(aInfo.iDstAddr).Ip6Address(); + + // + // Check various things, whether to actually accept the redirect + // + // .. did I send this packet? + if (!IsMyAddress(TInetAddr::Cast(aInfo.iSrcAddr).Ip6Address())) + return; // -- original source is not my address, just ignore the redirect + + // ..the gateway must be another node on the link + CIp6Route *route = FindRoute(gateway, NULL); + if (route == NULL || !(route->IsOnlink() || route->IsHostRoute())) + return; + + // ...check that destination would actually have been sent to the + // ...router that sent the ICMP redirect. + const TUint dstType = dst_addr.Scope()-1; + if (dstType > EScopeType_NET) + return; // -- bad scope value + route = Interfacer().FindRoute(dst_addr, iScope[dstType], dstType); + if (route == NULL || &route->iInterface != this || !route->IsGateway()) + return; // -- nope, not routed to a gateway on this interface + const TIpHeader *const ip = ((RMBufPacketPeek &)aPacket).GetIpHeader(); + if (!ip || ip->ip4.Version() != 4) + return; // -- probably bad packet + if (!TIp46Addr(ip->ip4.SrcAddr()).IsEqual(route->iAddress.Ip6Address())) + return; // -- would not be sent to source of the icmp redirect + + // + // Redirect accepted, do the stuff... + // + (void)GetRoute(gateway, 128, KRouteAdd_ISROUTER); // Must mark the gateway as ROUTER! + const TInetAddr gw(gateway, 0); + (void)GetRoute(dst_addr, 128, CIp6Route::ERedirect, &gw); +#ifdef _LOG + { + TBuf<70> tmpdst, tmpgw; + TInetAddr::Cast(aInfo.iDstAddr).OutputWithScope(tmpdst); + gw.OutputWithScope(tmpgw); + Log::Printf(_L("\tIF %u [%S] Redirecting IPv4 dst=[%S] to [%S]"), iScope[0], &iName, &tmpdst, &tmpgw); + } +#endif + // Previously, any flow using the rerouted destination address was assigned + // to the 'route'. Need to kick those flows to recheck their nexthop (e.g. + // all flows going to the 'destination' must now be assigned to the new + // redirected route + route->SetChanged(); + } + +// +// ******************************** +// MIfUser Interface Implementation +// ******************************** +// + +/** +// The NIF::BindL failed. +// +// A relic from ancient time -- not used currently. +// +// @deprecated +*/ +void CIp6NifUser::IfUserBindFailure(TInt aResult, TAny* aId) + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserBindFailure(%d, %d)"), &LogName(), aResult, (TInt)aId)); + + // Remove warnings + (void) aResult; + (void) aId; + } + +/** +// Introduce a new interface. +// +// @param aIf The NIF +// @param aId A relic from history -- not used currently. +*/ +void CIp6NifUser::IfUserNewInterfaceL(CNifIfBase* aIf, TAny* aId) + { + (void) aId; // Remove warning + + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserNewInterface(%d, %d)"), &LogName(), (TInt)aIf, (TInt)aId)); + if (!aIf) + return; // Should do something? Does this ever happen? + TNifIfInfo info; + aIf->Info(info); + CIp6Interface *iface = iManager.GetInterfaceByNameL(info.iName); + if (iface->IsNetdial()) + User::Leave(KErrBadDriver); // Interface is giving bad name ('') + + LOG(Log::Printf(_L("CIp6NifUser[%S]::AddInterface(CIp6Interface[%S])"), &LogName(), &iface->iName)); + aIf->Open(); // Prevent aIf from being deleted while in DoBind. + (void)iface->DoBind(this, aIf); + aIf->Close(); // *NOTE* This will delete the CNifIfBase instance + // if there was no other references to it! + } + +/** +// Reports closed interface using negative error code +// Reports status of operable interface using positive status code +// +// @param aResult Error code of closed interface or status of operable interface +// @param aIf The NIF +*/ +void CIp6NifUser::IfUserInterfaceDown(TInt aResult, CNifIfBase* aIf) + { + + CIp6Interface *const iface = iManager.FindInterface(aIf); + // + // IfUserInterfaceDown can only be processed, if the stack has an + // instance of CIp6Interface that is actually connected to the aIf + // (IfUserNewInterface has been called for it!). The above find + // returns non-NULL iface only iff "iface->iNifIf == aIf". + // + if (iface == NULL) + { + // NIFMAN is reporting interface down, which has no + // corresponding instance within the stack -- ignore + // silently... [this may happen if interface has been + // deleted from the stack, for example via ifconfig + // socket options] + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserInterfaceDown(%d, %d) NOT FOUND"), &LogName(), aResult, (TInt)aIf)); + return; + } + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserInterfaceDown(%d, %d) is CIp6Interface[%S])"), &LogName(), aResult, (TInt)aIf, &iface->iName)); + + const TInt link_change = (aResult == KErrLinkConfigChanged); + if (!link_change && aResult <= 0) + { + // First notify the error state to all flows attached to + // this interface. This will affect all flows that don't + // the flag iNoInterfaceError set. + iface->NotifyFlows(aResult); + } + else if (aResult > 0) + { + // IfUserInterfaceDown positive values are used to report NIF events to stack. + switch (aResult) + { + case KLinkLayerOpen: + if (iface->iIsSuspended) + { + // Flip flows to trigger CanSend() + iface->NotifyFlows(EFlow_HOLD, ETrue); + iface->NotifyFlows(EFlow_READY, ETrue); + iface->iIsSuspended = FALSE; + } + break; + case KDataTransferTemporarilyBlocked: + iface->iIsSuspended = TRUE; + break; + } + // Just return here when processing positive values + return; + } + // Note: if there is no holding route, flows will be terminated + // by the Reset(). Currently, holding *should* always exist. + for (CIp6Route *rt = iface->iRouteList; rt != NULL; rt = rt->iNext) + Interfacer().MoveToHolding(*rt); + + if(link_change) + { + // In case link is changed, just reset the interface but do not delete it. + iface->Reset(link_change); + } + else + { + // Delete interface instance when it goes down. + Interfacer().RemoveInterface(iface); + } + + if (iNetwork) + iNetwork->Protocol()->Error(aResult, NULL); + } + +void CIp6NifUser::IfUserOpenNetworkLayer() + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserOpenNetworkLayer()"), &LogName())); + __ASSERT_ALWAYS(iNetwork, User::Invariant()); + iNetwork->Protocol()->Open(); + iManager.iNifCount++; + iManager.IncUsers(); + } + +void CIp6NifUser::IfUserCloseNetworkLayer() + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserCloseNetworkLayer()"), &LogName())); + __ASSERT_ALWAYS(iNetwork, User::Invariant()); + --iManager.iNifCount; + iManager.DecUsers(); + iNetwork->Protocol()->Close(); + } + +CProtocolBase* CIp6NifUser::IfUserProtocol() + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserProtocol() --> %d"), &LogName(), (TInt)iNetwork)); + return iNetwork ? iNetwork->Protocol() : NULL; + } + +TBool CIp6NifUser::IfUserIsNetworkLayerActive() + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserIsNetworkLayerActive() iUsers=%d, iNifCount=%d --> %d"), + &LogName(), iManager.iUsers, iManager.iNifCount, (iManager.iUsers - iManager.iNifCount) > 0)); + return (iManager.iUsers - iManager.iNifCount) > 0; + } + +TBool CIp6NifUser::IfUserIsNetworkLayerActive(CNifIfBase *aIf) + { + if (aIf == NULL) + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserIsNetworkLayerActive(NULL) *BUG IN NIFMAN* "), &LogName())); + return IfUserIsNetworkLayerActive(); + } + CIp6Interface *const iface = iManager.FindInterface(aIf); + if (iface == NULL) + { + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserIsNetworkLayerActive(%d) *BUG IN NIFMAN"), &LogName(), (TInt)aIf)); + return IfUserIsNetworkLayerActive(); + } + LOG(Log::Printf(_L("CIp6NifUser[%S]::IfUserIsNetworkLayerActive(%S) returns %d"), + &LogName(), &iface->iName, iface->iFlows > 0)); + return iface->iFlows > 0; + } +#ifdef SYMBIAN_TCPIPDHCP_UPDATE +/** +// Do DAD for my blobal address +// Ref: RFC 4862 +// @param aPrefix The prefix of an IP address,part of prefix information sent by RA +// @param aLength The prefix length,part of prefix information sent by RA +*/ +void CIp6Interface::PerformDADForGlobalAddress(const TIp6Addr &aPrefix,const TUint aLength) + { + #ifdef _DEBUG + LOG(Log::Printf(_L("\tCIp6Interface::PerformDADForGlobalAddress is called"))); + #endif + if (&aPrefix == NULL) + return; + + TInetAddressInfo info; + info.iAddress = aPrefix; + MakeFullAddress(info.iAddress, aLength,iAddress.iId.u.iAddr8, sizeof(iAddress.iId.u.iAddr8)); + #ifdef _DEBUG + LOG(Log::Printf(_L("\tCIp6Interface::PerformDADForGlobalAddress,MakeFullAddress done"))); + #endif + TIp6AddressInfo *address; + // check info.iAddress matches any of the id's for the interface (also tentative ones!) + address = IsMyId(info.iAddress); + if(address == NULL) + { + #ifdef _DEBUG + LOG(Log::Printf(_L("\tCIp6Interface::PerformDADForGlobalAddress,address is NULL so returning from the method"))); + #endif + return; + } + if (!address->IsTentative()) + { + #ifdef _DEBUG + LOG(Log::Printf(_L("\tCIp6Interface::PerformDADForGlobalAddress,sending NS"))); + #endif + SendNeighbors(KInet6ICMP_NeighborSol, NULL,info.iAddress,&KInet6AddrNone); + } + else + { + //it is not my address ignore it + #ifdef _DEBUG + LOG(Log::Printf(_L("\tCIp6Interface::PerformDADForGlobalAddress,address->IsTentative()"))); + #endif + return; + } + } +#endif // SYMBIAN_TCPIPDHCP_UPDATE + +#ifdef __ARMCC__ +#pragma pop +#endif