networkprotocols/tcpipv4v6prt/src/iface.cpp
changeset 0 af10295192d8
child 5 1422c6cd3f0c
--- /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 <comms-infras/nifif.h>.
+//
+#include <e32hal.h>
+#include <e32math.h>
+#include <nifman.h>
+#include <comms-infras/nifif.h> // ..for CNifIfBase in Epoc R6 and later
+
+#include <in6_opt.h>
+#include <in_sock.h>
+#include "inet6log.h"
+#include "iface.h"
+#include <in6_if.h>     // IPv6 driver API specifications
+#include <icmp6_hdr.h>
+#include <in_chk.h>
+#include <ip6_hook.h>
+#include "in_flow.h"
+#include <timeout.h>
+#include <inet6err.h>
+#include "addr46.h"
+#ifdef ARP
+#include <arp_hdr.h>
+#endif
+
+#include <es_ini.h>
+
+#ifdef SYMBIAN_NETWORKING_UPS
+#include "in_trans.h"
+#endif
+
+#include "tcpip_ini.h"
+#include "networkinfo.h"
+#include <in6_event.h>
+#include <in6_dstcache.h>
+
+#include "in6_version.h"
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <in_sock_internal.h>
+#include <in6_dstcache_internal.h>
+#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 <agenterrors.h>
+#else
+#   define  KErrLinkConfigChanged (-3060)
+#endif
+
+#include <comms-infras/nifif_internal.h>
+#include <nifman_internal.h>
+
+//
+// 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<CIp6Manager, KIp6ManagerTimeoutOffset>
+    /**
+    * 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;rdnssIndex<numRdnssEntry;rdnssIndex++)
+        {   
+        rdnssEntry = GetRdnssEntryRef(rdnssIndex);
+        TInetAddr inetAddr(rdnssEntry.iRDNSSaddress);
+        if(aAddress.IsEqual(TInetAddr::Cast(inetAddr).Ip6Address()))
+            {
+            aArrIndex = rdnssIndex;
+            return ETrue;
+            }
+        else
+            {
+            continue;   
+            }
+        }
+    return EFalse;
+    }
+
+
+// RdnssOrderFunc
+// ****************************
+// Function used to Order the List in Descending order
+TInt RdnssOrderFunc( const TRdnssSortData &a, const TRdnssSortData &b)
+    {
+    if(a.iStoredRdnssLifeTime != b.iStoredRdnssLifeTime)
+        {
+        return a.iStoredRdnssLifeTime >b.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<TRdnssSortData> 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<rdnssCount;rdnssIndex++)
+        {
+        rdnssEntry = GetRdnssEntryRef(rdnssIndex);
+        // Insert index position of iRdnssArrayList[] into iRdnssLifetimeArrayList[]      
+        lifetimeArrayList.iRdnssServerListIndex = rdnssIndex;
+        lifetimeArrayList.iStoredRdnssLifeTime  = rdnssEntry.iStoredRdnssLifeTime;
+        // Insert copied entries into iRdnssLifetimeArrayList
+        iRdnssLifetimeArrayList.InsertL(lifetimeArrayList,rdnssIndex);
+        }
+    }
+
+
+// CManageRdnssServerList::GetRdnssLifetimeEntryRef
+// ****************************
+// Gets a reference to Lifetime entry in  RDNSS Lifetime list
+TRdnssSortData& CManageRdnssServerList::GetRdnssLifetimeEntryRef(TInt aIndex)
+    {
+    return iRdnssLifetimeArrayList[aIndex];      
+    }
+
+
+// CManageRdnssServerList::DeleteRdnssLifetimeEntry
+// ****************************
+// Delete Lifetime Entry for corresponding index in RDNSS Lifetime list
+void CManageRdnssServerList::DeleteRdnssLifetimeEntry(TInt aRdnssArrayIndex)
+    {
+    // Removes entry from the array iRdnssLifetimeArrayList
+    iRdnssLifetimeArrayList.Remove(aRdnssArrayIndex); 
+    }
+
+
+// CManageRdnssServerList::RdnssLifetimeListDelete
+// ****************************
+// Delete All Lifetime Entries in Rdnss Lifetime List 
+void CManageRdnssServerList::RdnssLifetimeListDelete()
+    {
+    TInt rdnssCount;
+    rdnssCount=CountRdnssLifetimeEntry();
+    for(;rdnssCount!=0;rdnssCount--)
+        {
+        DeleteRdnssLifetimeEntry(0); // 0 - Remove all Entries in RdnssDummyArrayList        
+        
+#ifdef _DEBUG
+        LOG(Log::Printf(_L("\t ...RdnssLifetimeListDelete.[Deleted Entry: %d]"),rdnssCount));
+#endif      
+        }   
+    }
+
+
+// CManageRdnssServerList::PrintRdnssLifetimeList
+// ****************************
+// Prints Lifetime Entries associated with iRdnssServerList Table 
+void CManageRdnssServerList::PrintRdnssLifetimeList(TUint8 aIndex)
+    {
+    TRdnssSortData rdnssEntry = GetRdnssLifetimeEntryRef(aIndex);
+#ifdef _DEBUG       
+    LOG(Log::Printf(_L("\t [Index:%d],[RDNSSIndex =%d],[Lifetime=%d]"),aIndex, rdnssEntry.iRdnssServerListIndex,rdnssEntry.iStoredRdnssLifeTime));
+#endif    
+    }
+
+
+// CManageRdnssServerList::RdnssExpireLeastEntry
+// ****************************
+// Expire an RDNSS Entry with Least Lifetime in RDNSS Server List
+void CManageRdnssServerList::RdnssExpireLeastEntry(TRdnssOptionData aRdnssRcvData)
+    {
+    // Copy the contents from iRdnssArrayList to iRdnssLifetimeArrayList and perform sort on later
+    // to find Worse Lifetime Entry corresponding to StoredLifetime. 
+    
+    RdnssServerListCopyL();    
+    
+    //Sort iRdnssLifetimeArrayList using RArray Sort.
+    RdnssServerListSort();    
+    
+    //Get the Least Lifetime entry available at Index 0
+    TRdnssSortData rdnssData = GetRdnssLifetimeEntryRef(0);    
+    TInt rdnssServerListIndex = rdnssData.iRdnssServerListIndex;      
+    DeleteRdnssEntry(rdnssServerListIndex);    
+    LOG(Log::Printf(_L("\t Deleted an Entry and Insert Successful")));
+    InsertRdnssEntryL(aRdnssRcvData, 0);
+#ifdef _DEBUG    
+    for(TUint8 index=0;index<CountRdnssLifetimeEntry();index++)
+        {   
+        PrintRdnssLifetimeList(index);      
+        }    
+#endif
+    // Flush all the entries in iRdnssLifetimeArrayList, since its no more required
+    RdnssLifetimeListDelete();
+    }
+
+
+// CManageRdnssServerList::RdnssServerListDelete
+// ****************************
+// Expire All RDNSS Entries in RDNSS Server List since router lifetime is 0
+void CManageRdnssServerList::RdnssServerListDelete()
+    {
+    TInt rdnssCount;
+    rdnssCount=CountRdnssEntry();
+    for(;rdnssCount!=0;rdnssCount--)
+        {
+        DeleteRdnssEntry(0); // 0 , since its last entry in iRdnssServerList        
+#ifdef _DEBUG
+        LOG(Log::Printf(_L("\t ...RdnssServerListDelete.[Deleted Entry: %d]"),rdnssCount));
+#endif      
+        }   
+    }
+
+
+// CManageRdnssServerList::RdnssServerListSync
+// ****************************
+// Syncrhonises RDNSS server list 
+TBool CManageRdnssServerList::RdnssServerListSync(TInetAddr& aNameSer1, TInetAddr& aNameSer2)
+    {
+    // Verfifies elapsed Lifetime is equal to 0 for each RDNSS Entry in iRdnssArrayList.
+    // If elapsed Lifetime is zero, then delete corresponding entry in iRdnssArrayList.
+    // If entry corresponds to RDNSS NameServer Repository[0 or 1], 
+    // Synchronise NameServer Repository Entries in iRdnssArrayList and update iNameSer1 and iNameSer2 entries in CIp6Interface
+    
+    TRdnssLifetime elapsedLifetime;
+    TRdnssOptionData rdnssEntry;
+    TBool sendRdnssRS = EFalse;    
+        
+    for(TInt rdnssIndex=0;rdnssIndex<CountRdnssEntry();++rdnssIndex)
+        {   
+            rdnssEntry = GetRdnssEntryRef(rdnssIndex);
+            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      
+            // 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<CountRdnssEntry();index++)
+        {   
+        PrintRdnssServerList(index);
+        }
+#endif  
+    }
+
+
+// CManageRdnssServerList::RdnssParseOptionHdr
+// ****************************
+// Parse received RDNSS Option Header from RA.
+// Returns True, if option length is > 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<TIcmpNdHeader>  &aNd);
+#ifdef ARP
+    TInt ArpHandler(RMBufRecvPacket &aPacket, RMBufRecvInfo &aInfo, TInet6Packet<TInet6HeaderArp> &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<CIp6Interface, KIp6InterfaceTimeoutOffset>
+    {
+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<CIp6Route, KIp6RouteTimeoutOffset>
+    {
+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<const TDesC &>(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<TSoIfConnectionInfo> 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<KMaxArgLen> 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<TIp6Mreq> 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<TSoInetIfConfig> &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<TSoIfInfo6> 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<TSoInet6IfConfig> 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 <tt>96 < aMaskLength <= 128</tt>.
+//
+// @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<TIp6AddressInfo *>( 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<TSoIfInfo> 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<TSoInetIfConfig> 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 <TInet6HeaderArp> 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<TInet6HeaderICMP_NeighborAdv> 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<TSoIfConnectionInfo> 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<TSoIfHardwareAddr> 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;index<arrayCount;index++)
+                    {   
+                     iRdnssList->PrintRdnssServerList(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<TSoInetIfConfig> 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<TInetInterfaceInfo> 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<TInetAddressInfo> 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<TInetRouteInfo> 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<TIcmpNdHeader> 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<TIcmpNdHeader>  &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<TInet6HeaderArp> 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<TInet6HeaderArp> &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