--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/networkprotocols/tcpipv4v6prt/src/ip6.cpp Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,3478 @@
+// Copyright (c) 2006-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:
+// ip6.cpp - IPv6 protocol
+//
+
+#include "inet6log.h"
+#include <ip6_hdr.h>
+#include <ip4_hdr.h>
+#include <icmp6_hdr.h>
+#include <udp_hdr.h>
+#include <in_pkt.h>
+#include <in_chk.h>
+#include <in6_if.h> // only for KIpBroadcastOnLink
+#include "ip6.h"
+#include <ip6_hook.h>
+#include <ip6_iprt.h>
+#include "ip6_rth.h"
+#include "ip6_doh.h"
+#include "ip6_frag.h"
+#include "in_flow.h"
+#include "iface.h"
+#include "in_net.h"
+#include <ext_hdr.h>
+#include <inet6err.h>
+#include "loop6.h"
+#include "res.h" // only for KProtocolInet6Res
+#include "addr46.h"
+#include "tcpip_ini.h"
+#include <comms-infras/nifif_internal.h>
+
+// The preprocessor symbol: ARP
+// ----------------------------
+// Add code for doing IPv4 Address Resolution Protocol (ARP) on
+// IPv4 interfaces that specify "NeedNd". (also needed in ïface.cpp)
+
+#ifdef ARP
+#include <arp_hdr.h>
+#endif
+
+static const TLitC8<sizeof(TInt)> KInetOptionDisable = {sizeof(TInt), {0}};
+
+//
+// ****************
+// TUpperLayerSnoop
+// ****************
+// A simple class to map the first 4 bytes of the packet into
+// ports or icmp type/code. This is used to snoop the upper
+// layer information. (The use of "real" upper layer header
+// classes directly would complicate things: each protocol
+// would need to have a separate mapping)
+//
+class TUpperLayerSnoop
+ {
+ public:
+ //
+ // Basic
+ //
+ inline static TInt MinHeaderLength() {return 4; }
+ inline static TInt MaxHeaderLength() {return 4; }
+
+ union
+ {
+
+ //
+ // The same mapping will do for both TCP and UDP,
+ // as only port fields are really accessed.
+ //
+ TInet6HeaderUDP udp;
+ TInet6HeaderICMP icmp;
+ };
+ };
+
+//
+//
+// CProtocolIP6
+// ************
+// The implementation and even the class definition of CProtocolIP6 is
+// totally internal to ip6.cpp. No other module needs to know any of the
+// internals. Thus, all declarations of the CProtocolIP6 are included here
+//
+//
+class CHookEntry;
+class THookList
+ {
+ friend class CProtocolIP;
+ friend class CProtocolIP4;
+ friend class CProtocolIP6;
+ void AddL(CProtocolBase *aProtocol);
+ void AddL(CIp6Hook *aHook, TInt aPriority);
+ TInt AddByOrderListL(CIp6Hook *aHook, const TDesC &aName, const TDesC &aOrdering, const TInt aPriority);
+ TInt Remove(const CProtocolBase *const aProtocol);
+ TInt Remove(const CIp6Hook *const aHook);
+ TInt Remove(const TAny *const any);
+ TInt RemoveAll();
+ void StartSending(CProtocolBase *aSource);
+ void StartSending(CProtocolBase *aIface, CProtocolBase *aSrc);
+ void Error(TInt aError, CProtocolBase *aSource);
+ void Error(TInt aError, CProtocolBase *aIface, CProtocolBase *aSrc);
+ void InterfaceAttached(const TDesC &aName, CNifIfBase *aIf);
+ void InterfaceDetached(const TDesC &aName, CNifIfBase *aIf);
+private:
+ void Delink(CHookEntry *p, CHookEntry *h);
+ void Link(CHookEntry *p, CHookEntry *h);
+ CHookEntry *iHead;
+ TUint iChainId;
+ };
+
+
+//
+// TBoundHookEntry
+// ---------------
+// Bookkeeping entry for the protocols that are bound via
+// esock.ini directive to the IP6 instance
+//
+class TBoundHookEntry
+ {
+public:
+ CProtocolBaseUnbind *iProtocol; // Bound protocol, always non-NULL!
+ CNifIfBase *iInterface; // Interface, non-NULL for a special "interface protocol"
+ };
+
+//
+// TIcmpThrottle
+// -------------
+//
+class TIcmpThrottle
+ {
+public:
+ TIcmpThrottle() :
+// iMask((TUint32)(~((1 << 26) - 1))), // mask matching about 67 seconds
+ iMask((TUint32)(~((1 << 23) - 1))), // mask matching about 8 seconds
+ iStamp(0)
+ {}
+ TInt Suppress();
+ inline void SetMax(TInt aMax) { iMax = aMax; }
+private:
+ const TUint32 iMask;
+ TUint32 iStamp;
+ TInt iMax, iCount;
+ };
+
+TInt TIcmpThrottle::Suppress()
+ {
+ TTime now;
+ //
+ // The idea is to have a low overhead throttle test by
+ // defining the interval as a 2**N of microseconds
+ // (2**23 ~ 8 seconds), and just using the remaining
+ // high order bits as a time stamp, which when changed
+ // allows more to send... (this will allow a burst of
+ // of iMax messages, in the beginning of time interval!)
+ //
+ now.UniversalTime();
+#ifdef I64LOW
+ TUint ref = (TUint)I64LOW(now.Int64()) & iMask;
+#else
+ TUint ref = now.Int64().Low() & iMask;
+#endif
+ if (ref != iStamp)
+ {
+ iStamp = ref;
+ iCount = 0;
+ }
+ return ++iCount > iMax;
+ }
+
+// TPacketContextItem
+// ******************
+class TPacketContextItem
+ {
+public:
+ TUint32 iKey, iValue;
+ };
+
+class CProtocolIP : public CProtocolInet6Network, public MNetworkServiceExtension, public MPacketContext
+ {
+ friend class IP6;
+public:
+ CProtocolIP(CIfManager *aInterfacer, TInt aProtocol);
+ ~CProtocolIP();
+ void InitL(TDesC& aTag);
+ CServProviderBase* NewSAPL(TUint aSockType);
+ void BindToL(CProtocolBase *protocol);
+ void BindL(CProtocolBase *protocol, TUint id);
+ void StartL(void);
+ void Process(RMBufChain &aPacket, CProtocolBase* aInterface);
+ void Unbind(CProtocolBase *protocol, TUint id = 0);
+ void Identify(TServerProtocolDesc *) const;
+ TInt GetOption(TUint level,TUint name,TDes8 &option,CProtocolBase* aSourceProtocol);
+ TInt SetOption(TUint level, TUint aName,const TDesC8 &option,CProtocolBase* aSourceProtocol);
+ //
+ // Active users tracking is done by the interface manager,
+ // just pass the following directly.
+ //
+ void IncUsers() { iInterfacer->IncUsers(); }
+ void DecUsers() { iInterfacer->DecUsers(); }
+
+
+ void Error(TInt aError, CProtocolBase* aSrc);
+ void StartSending(CProtocolBase* aSrc);
+
+ //
+ // Network service (MNetworkService)
+ //
+ CProtocolInet6Binder *Protocol() const;
+ MInterfaceManager *Interfacer() const;
+ TInt Send(RMBufChain &aPacket, CProtocolBase* aSrc = NULL);
+ void Icmp4Send(RMBufRecvPacket &aPacket, TInt aType, TInt aCode, TUint32 aParameter, TInt aMC = 0);
+ void Icmp6Send(RMBufRecvPacket &aPacket, TInt aType, TInt aCode, TUint32 aParameter, TInt aMC = 0);
+ void IcmpWrap(RMBufChain &aPacket, const TIcmpTypeCode aIcmp, const TUint32 aParameter = 0, const TInt aMC = 0);
+
+ TBool Fragment(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo, TInt aMtu, RMBufPktQ &aFragments);
+
+ CHostResolvProvdBase *NewHostResolverL();
+ CServiceResolvProvdBase *NewServiceResolverL();
+ CNetDBProvdBase *NewNetDatabaseL();
+
+ //
+ // Network service extension (additional methods MNetworkServiceExtension)
+ //
+ void InterfaceAttached(const TDesC &aName, CNifIfBase *aIf);
+ void InterfaceDetached(const TDesC &aName, CNifIfBase *aIf);
+
+
+ //
+ // The Flow Manager section (MFlowManager)
+ //
+ CFlowContext *NewFlowL(const void *aOwner, TUint aProtocol);
+ CFlowContext *NewFlowL(const void *aOwner, CFlowContext &aFlow);
+ TInt SetChanged() const;
+ TInt FlowSetupHooks(CFlowInternalContext &aFlow);
+ void FlowStartRefresh(CFlowInternalContext &aFlow);
+ TInt GetFlowOption(TUint aLevel, TUint aName, TDes8 &aOption, const CFlowContext &aFlow) const;
+ TInt SetFlowOption(TUint aLevel, TUint aName, const TDesC8 &aOption, CFlowContext &aFlow);
+ //
+ // The Packet Context (MPacketContext)
+ //
+ TInt SetHookValue(const TUint32 aId, const TUint32 aValue);
+ TUint32 HookValue(const TUint32 aId) const;
+
+ TBool DoSwitchL(RMBufHookPacket &aPacket);
+ void PostProcess(RMBufChain &aPacket, CProtocolBase *aSrc);
+
+private:
+ void DoProcess();
+ void IcmpEcho(RMBufPacketBase &aPacket, RMBufRecvInfo *aInfo);
+ void IcmpSend(TInt aProtocol, RMBufRecvPacket &aPacket, TInt aType, TInt aCode, TUint32 aParameter, TInt aMC);
+ TPtrC HookOrdering(const TDesC &aOrderKey) const;
+ static TInt RecvCallBack(TAny* aProtocol);
+protected:
+ TInt IcmpHandlerL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo, CProtocolBase *aFinal);
+ void UnbindAll(TAny *aProtocol);
+ TInt DoBuild(RMBufPacketBase &aPacket, RMBufPktInfo &aInfo, TPacketHead &aHead);
+ void DoSendOnePacket(RMBufSendPacket &aPacket);
+ void DoProcessOnePacketL(RMBufHookPacket &aPacket);
+ CIfManager *const iInterfacer;
+ // Resolver instance
+ CProtocolBase *iResolver;
+ // IPv4 "slave" instance, if NON-NULL (catched in BindToL,
+ // not "reference counted"!).
+ CProtocolIP *iSlavedIP4;
+ //
+ // Protocol "identity", the following members contain either
+ // (KProtocolInetIp,KProtocolInetIcmp) for IPv4
+ // or
+ // (KProtocolInet6Ip, KProtocolInet6Icmp) for IPv4
+ // Both of these could always be deduced from the iProtocol of
+ // TServerProtocolDesc, but this way makes some shared code
+ // simpler to write.
+ const TInt iProtocol, iIcmpProtocol;
+ //
+ // A brute force protocol switch array (will possibly get revised,
+ // later if needed).This array is directly indexed by the next header
+ // value of the IP packet.
+ //
+ // Every element of the array will always have a valid pointer value
+ // assigned (iNullProtocol). At run time, there is never need to verify
+ // against a NULL pointer.
+ //
+ // Also, a pointer in iSwitch does not mean that CProtocolIP6 class
+ // owns that instance, the objects pointed by this array are NOT
+ // deleted when IP6 instance is destroyed.
+ //
+ MNifIfUser *iNifUser; // Defined when protocol is registered with the interfacer
+
+ THookList iSwitch[KIp6Hook_ANY+1];
+ TUint SwitchSize() {return sizeof(iSwitch) / sizeof(iSwitch[0]); }
+ CHookEntry *iNullHook;
+ CFragmentHeaderHook *iFragmentHeader;
+ CNifIfBase *iLoopback4; // "Hardcoded" IPv4 loopback interface
+ CNifIfBase *iLoopback6; // "Hardcoded" IPv6 loopback interface
+ //
+ // State information used by the ICMP "throttle"
+ //
+ TIcmpThrottle iIcmpThrottle;
+ // iBindCount counts how many non-NULL protocols are currently
+ // stored in iSwitch. This variably is purely for debugging
+ // purposes. It *should* be zero when this protocol instance is
+ // destroyed!
+ //
+ TInt iBindCount;
+ //
+ // List of hooks that are interested in peeking at the outbound packets
+ //
+ THookList iOutbound;
+ //
+ // List of hooks that are intrested in packets that got received by
+ // the stack, but which apparently are not addresses to this host
+ // (e.g. packets that may need forwarding).
+ //
+ THookList iForwardHooks;
+ //
+ // List of hooks that want to peek complete packets just before they
+ // are sent to the interface. These hooks must be aware of the iFlow
+ // in the info block, and are responsible to actully passing the packet
+ // to the interface. These hooks receive complete packets through the
+ // Send (Outbound) and Process (Inboud) interfaces.
+ //
+ THookList iPostInbound;
+ THookList iPostOutbound;
+ CIp6Hook *iPostTerminator;
+ //
+ // Some hooks may be installed by using the 'bindto'-directive of ESOCK.INI
+ // on IP6 instance. This will call BindtoL of IP6 to call Bind()+Open() to the
+ // indicated protocol. Need to keep track of these protocols and issue matching
+ // Close() calls in case IP6 is deleted. (NOTE: protocol bound in this way
+ // cannot "die" until IP6 releases them). iBoundHooks tracks these.
+ //
+ CArrayFixFlat<TBoundHookEntry> *iBoundHooks;
+ //
+ // Identification for the IPv4 header field
+ //
+ TInt iId;
+ //
+ // Packet forwarding section
+ //
+ TUint iForwardFlowSize; // Max Number of entries in the array
+ TUint iForwardFlowCount; // Current number of forward flow contexts.
+ RFlowContext *iForwardFlow; // Array of RFlowContext handles
+ TUint iForwardHits; // Packets forwarded without resetting a flow
+ TUint iForwardMiss; // Packets forwarded with resetting a flow
+ const TUint iSwitchSize;
+ //
+ // Work space for maintaining inbound packet context (during DoSwitch())
+ // (fixed allocation now, implement something else later if needed)
+ //
+ TInt iPacketContextCount; // Number of used entries
+ // .. the max number of slots is automaticly determined by the
+ // .. size of the array iPacketContext (see SetHookValue).
+ TPacketContextItem iPacketContext[8];
+ TUint32 iPacketId; // Packet sequence number (used as packet id).
+ //
+ // The queue for the received packets from the NIF's.
+ //
+ RMBufAsyncPktQ iRecvQ;
+ };
+
+class CProtocolIP6 : public CProtocolIP
+ {
+ friend class IP6;
+public:
+ CProtocolIP6(CIfManager *aInterfacer);
+ ~CProtocolIP6();
+ void InitL(TDesC& aTag);
+private:
+ CRoutingHeaderHook *iRoutingHeader;
+ CDestinationOptionsHook *iDestinationOptions;
+ CHopOptionsHook *iHopOptions;
+ };
+
+
+class CProtocolIP4 : public CProtocolIP
+ {
+ friend class IP6;
+public:
+ CProtocolIP4(CIfManager *aInterfacer);
+ ~CProtocolIP4();
+ void InitL(TDesC& aTag);
+ };
+
+//
+// IP6 Class Implementation
+//
+void IP6::Identify(TServerProtocolDesc &aEntry, TInt aVersion)
+ {
+ if (aVersion == STATIC_CAST(TInt, KProtocolInetIp))
+ {
+ aEntry.iName=_S("ip");
+ aEntry.iAddrFamily=KAfInet;
+ aEntry.iProtocol=KProtocolInetIp;
+ // message size is absolute upper limit, not guaranteed to work
+ aEntry.iMessageSize=0xffff-TInet6HeaderIP4::MinHeaderLength();
+ }
+ else
+ {
+ aEntry.iName=_S("ip6");
+ aEntry.iAddrFamily=KAfInet6;
+ aEntry.iProtocol=KProtocolInet6Ip;
+ // message size is absolute upper limit, not guaranteed to work
+ aEntry.iMessageSize=0xffff-TInet6HeaderIP::MinHeaderLength();
+ }
+ aEntry.iSockType=KSockDatagram;
+ aEntry.iVersion=TVersion(KInet6MajorVersionNumber, KInet6MinorVersionNumber, KInet6BuildVersionNumber);
+ aEntry.iByteOrder=EBigEndian;
+ aEntry.iServiceInfo=KIP6ServiceInfo;
+ aEntry.iNamingServices=KIP6NameServiceInfo;
+ aEntry.iSecurity=KSocketNoSecurity;
+ aEntry.iServiceTypeInfo=KIP6ServiceTypeInfo;
+ aEntry.iNumSockets=KUnlimitedSockets;
+ }
+
+CProtocolBase *IP6::NewL(CIfManager *aInterfacer, TInt aVersion)
+ {
+ CProtocolBase *prt;
+ if (aVersion == STATIC_CAST(TInt, KProtocolInetIp))
+ prt = new (ELeave) CProtocolIP4(aInterfacer);
+ else
+ prt = new (ELeave) CProtocolIP6(aInterfacer);
+ return prt;
+ }
+
+//
+// THookList
+// The implementation of this is totally internal to this IP6
+// protocol module. Thus, all declaratations and implementation
+// is here. No outside module needs to know how this works!
+//
+class CHookEntry : public CBase
+ {
+ friend class THookList;
+ friend class CProtocolIP;
+ friend class CProtocolIP4;
+ friend class CProtocolIP6;
+ CHookEntry(CProtocolBase *aProtocol, CHookEntry *aNext) : iType(0), iNext(aNext)
+ /** Construct upper layer protocol entry. */
+ { iProtocol = aProtocol; }
+ CHookEntry(CIp6Hook *aHook, CHookEntry *aNext, TInt aPriority) : iType(aPriority), iNext(aNext)
+ /** Construct a hook entry. */
+ { iHook = aHook; }
+ inline TBool IsHook() const { return iType != 0; }
+ inline TBool IsProtocol() const { return iType == 0; }
+private:
+ TInt iType;
+ union
+ {
+ CProtocolBase *iProtocol; //< if iType == 0,
+ CIp6Hook *iHook; //< if iType != 0, for inbound packet hook
+ };
+ class CHookEntry *iNext;
+ };
+
+
+void THookList::AddL(CProtocolBase *aProtocol)
+ /**
+ * Adds an upper layer protocol handler.
+ *
+ * Override the current protocol handler with a new
+ * protocol binding. The previous binding (if any) is not
+ * removed, but is hidden behind this new one until it unbinds.
+ *
+ * The new element is added in front of the old one.
+ *
+ * @param aProtocol The protocol
+ */
+ {
+ CHookEntry *p = NULL;
+ CHookEntry *h, **head = &iHead;
+ //
+ // Skip over possible hook mode handlers
+ //
+ while ((h = *head) != NULL && h->IsHook())
+ {
+ p = h;
+ head = &h->iNext;
+ }
+ //
+ // 'h' points the current first protocol (if it exists), just insert
+ // this new protocol in front of it and "hide" the previous one
+ //
+ // coverity[alloc_fn] coverity[assign]
+ *head = new (ELeave) CHookEntry(aProtocol, h);
+ Link(p, *head);
+ // coverity[memory_leak]
+ }
+
+void THookList::AddL(CIp6Hook *aHook, TInt aPriority)
+ /**
+ * Add a new hook handler into the list.
+ *
+ * @param aHook The hook
+ * @param aPriority The priority (must be > 0)
+ */
+ {
+ if (aPriority > 0)
+ {
+ CHookEntry *p = NULL;
+ CHookEntry *h, **head = &iHead;
+ while ((h = *head) != NULL && h->iType > 0 && h->iType > aPriority)
+ {
+ p = h;
+ head = &h->iNext;
+ }
+ // coverity[alloc_fn] coverity[assign]
+ *head = new (ELeave) CHookEntry(aHook, h, aPriority);
+ Link(p, *head);
+ }
+ // coverity[memory_leak]
+ }
+
+// THookList::AddByOrderList
+// *************************
+// Add protocol to list based on the ordering.
+//
+// The priority is computed by (bigger is better):
+//
+// (10000 - hook position in the list) * 256 + aPriority
+//
+// If the protocol is mentioned more than once in the aOrdering,
+// and if list is not chained, then the protocol will be added
+// multiple times.
+//
+// Returns the change in iBindCount (usually +1).
+//
+// *WARNING*
+// If protocol is addedd multiple times and the latter AddL
+// leaves, then the iBindCount will be incorrect. [to keep
+// count correct in such case is too much trouble and failing
+// to add is pretty serious problem which most likely shuts
+// down the stack anyway... -- msa]
+//
+//
+TInt THookList::AddByOrderListL(CIp6Hook *aHook, const TDesC &aName, const TDesC &aOrdering, const TInt aPriority)
+ {
+ TInt star = 0;
+ TInt slot = 10000 << 8;
+
+ // Only one "BindL" call per protocol/list allowed. Remove
+ // all previous bindings before starting to process this
+ // new set.
+ TInt bindcount = -Remove((TAny *)aHook);
+
+ TLex start(aOrdering);
+ for (TInt done = 0;;)
+ {
+ slot -= 256;
+ if (start.Eos())
+ {
+ if (!done)
+ {
+ // Protocol has not been specially mentioned in the ordering.
+ // Add it into the '*'-position (or, if there is none specified
+ // assume star at the end of the list).
+ AddL(aHook, aPriority + (star ? star : slot));
+ bindcount++;
+ }
+ break;
+ }
+ // *NOTE/WARNING* This is a simple parsing,
+ // no extra white space is accepted!
+ start.Mark();
+ while (!start.Eos() && start.Peek() != ',')
+ start.Inc();
+ TPtrC hook = start.MarkedToken();
+ if (!start.Eos())
+ start.Inc(); // Skip ','
+
+ if (hook.Compare(_L("*")) == 0)
+ star = slot;
+ else if (hook.CompareF(aName) == 0)
+ {
+ // Located the specific priority entry
+ AddL(aHook, slot + aPriority);
+ done = 1;
+ bindcount++;
+ if (iChainId)
+ // If hook list is chained, protocol can only be
+ // added once!
+ break;
+ }
+ }
+ return bindcount;
+ }
+
+//
+// THookList::Delink
+// *****************
+// The hook 'h' is has been removed from a list, where it
+// was located after 'p'. If this is chained list, then
+// updatate the bindings.
+//
+// p == NULL, if h was the first hook
+// p->iNext is already updated to point the hook
+// the follows h (or NULL, if none).
+//
+void THookList::Delink(CHookEntry *p, CHookEntry *h)
+ {
+ if (iChainId)
+ {
+ //
+ // If h was not the first in the list, then
+ // need to remove the chaining to h from the
+ // previous hook.
+ if (p)
+ p->iHook->Unbind(h->iHook, iChainId);
+ //
+ // If h was not the last in the list, then
+ // need to remove the chaining from 'h'
+ // to the next hook
+ if (h->iNext)
+ h->iHook->Unbind(h->iNext->iHook, iChainId);
+ //
+ // If h was not the first hook, and there was
+ // another hook following h, then must link
+ // the previous hook (p) to the following.
+ if (p && h->iNext)
+ {
+ TRAP_IGNORE(p->iHook->BindL(h->iNext->iHook, iChainId));
+ }
+ }
+ }
+
+// THookList::Link
+// ***************
+// The hook 'h' has been added to the list after 'p'
+//
+// p == NULL, if has was added to the front
+//
+void THookList::Link(CHookEntry *p, CHookEntry *h)
+ {
+ if (iChainId)
+ {
+ TInt err;
+ //
+ // If h was added after p and there
+ // is a hook after h, then the chaining from
+ // p must be unbound.
+ if (p && h->iNext)
+ p->iHook->Unbind(h->iNext->iHook, iChainId);
+ //
+ // If h is now followed by another hook, then must link
+ // h to this.
+ if (h->iNext)
+ {
+ TRAP(err, h->iHook->BindL(h->iNext->iHook, iChainId));
+ }
+ //
+ // If h was not the first hook, then must link the previous
+ // hook to h.
+ if (p)
+ {
+ TRAP(err, p->iHook->BindL(h->iHook, iChainId));
+ }
+ }
+ }
+
+
+//
+// THookList::Remove
+// Remove one specific protocol binding from the list.
+// Returns 1, if found and removed
+// Returns 0, if not found
+//
+TInt THookList::Remove(const CProtocolBase *const aProtocol)
+ {
+ CHookEntry **head = &iHead;
+ CHookEntry *p = NULL;
+ for (CHookEntry *h; (h = *head) != NULL; p = h, head = &h->iNext)
+ if (h->IsProtocol() && h->iProtocol == aProtocol)
+ {
+ *head = h->iNext;
+ Delink(p, h);
+ delete h;
+ return 1;
+ }
+ return 0;
+ }
+
+//
+// CHookList::Remove
+// Remove one specific hook binding from the list
+// Returns 1, if found and removed
+// Returns 0, if not found
+//
+TInt THookList::Remove(const CIp6Hook *const aHook)
+ {
+ CHookEntry **head = &iHead;
+ CHookEntry *p = NULL;
+ for (CHookEntry *h; (h = *head)->IsHook(); p = h, head = &h->iNext)
+ if (h->iHook == aHook)
+ {
+ *head = h->iNext;
+ Delink(p, h);
+ delete h;
+ return 1;
+ }
+ return 0;
+ }
+
+//
+// THookList::Remove
+// Remove all bindings to the specified protocol or hook.
+// Returns the number of of bindings removed.
+//
+TInt THookList::Remove(const TAny *const any)
+ {
+ TInt count = 0;
+ CHookEntry **head = &iHead;
+ CHookEntry *p = NULL;
+ for (CHookEntry *h; (h = *head) != NULL; )
+ if (h->iHook == any)
+ {
+ *head = h->iNext;
+ Delink(p, h);
+ delete h;
+ ++count;
+ }
+ else
+ {
+ p = h;
+ head = &h->iNext;
+ }
+ return count;
+ }
+
+// THookList::RemoveAll
+// Remove all entries from the list and
+// return the number of removed entries
+//
+TInt THookList::RemoveAll()
+ {
+ TInt count = 0;
+
+ CHookEntry *h;
+
+ while ((h = iHead) != NULL)
+ {
+ iHead = h->iNext;
+ Delink(NULL, h);
+ delete h;
+ ++count;
+ }
+ return count;
+ }
+
+//
+// THookList::StartSending/Error
+//
+// Deliver upcall for a target protocol
+// (Hooks are not informed currently)
+
+void THookList::StartSending(CProtocolBase *aSrc)
+ {
+ for (CHookEntry *h = iHead; h; h = h->iNext)
+ if (h->IsProtocol() && h->iProtocol != aSrc)
+ {
+ h->iProtocol->StartSending(NULL);
+ break;
+ }
+ }
+
+void THookList::Error(TInt aError, CProtocolBase *aSrc)
+ {
+ for (CHookEntry *h = iHead; h; h = h->iNext)
+ if (h->IsProtocol() && h->iProtocol != aSrc)
+ {
+ h->iProtocol->Error(aError, NULL);
+ break;
+ }
+ }
+
+void THookList::InterfaceAttached(const TDesC &aName, CNifIfBase *aIf)
+ {
+ for (CHookEntry *h = iHead; h; h = h->iNext)
+ if (h->IsHook())
+ h->iHook->InterfaceAttached(aName, aIf);
+ }
+
+void THookList::InterfaceDetached(const TDesC &aName, CNifIfBase *aIf)
+ {
+ for (CHookEntry *h = iHead; h; h = h->iNext)
+ if (h->IsHook())
+ h->iHook->InterfaceDetached(aName, aIf);
+ }
+
+void THookList::StartSending(CProtocolBase *aIface, CProtocolBase *aSrc)
+ {
+ for (CHookEntry *h = iHead; h; h = h->iNext)
+ if (h->IsHook() && h->iProtocol != aSrc)
+ h->iHook->StartSending(aIface);
+ }
+
+void THookList::Error(TInt aError, CProtocolBase *aIface, CProtocolBase *aSrc)
+ {
+ for (CHookEntry *h = iHead; h; h = h->iNext)
+ if (h->IsHook() && h->iProtocol != aSrc)
+ h->iHook->Error(aError, aIface);
+ }
+
+//
+// CProtocolIP6Null
+//
+// CProtocolIP6Null is purely internal to the ip6.cpp
+// implementation. It is used as a dummy protocol instance
+// which will receive all packets which have unbound protocol
+// in their next header field. This is *NOT* a visible protocol
+// for the socket manager.
+//
+// *HOWEVER* This is somewhat dubious, drags in some stuff from
+// CProtocolBase. Should use some intermediate class?
+//
+class CProtocolIP6Null : public CProtocolBase
+ {
+public:
+ CProtocolIP6Null(MNetworkService *aIp) : iIp(aIp) {}
+ void Identify(TServerProtocolDesc *) const {Panic(EInet6Panic_NotSupported);}
+ void Process(RMBufChain &aPacket,CProtocolBase *)
+ {
+ RMBufRecvPacket packet;
+ packet.Assign(aPacket);
+ RMBufRecvInfo *const info = packet.Unpack();
+ if (//
+ // Drop unhandled ICMP errors silently
+ //
+ info->iIcmp == 0 &&
+ //
+ // Drop packets with No Next Header silently
+ //
+ info->iProtocol != STATIC_CAST(TInt, KProtocolInet6NoNextHeader) &&
+ //
+ // Drop all ICMP's when there is no upper
+ // layer ICMP protocol to receive them.
+ //
+ info->iProtocol != STATIC_CAST(TInt, KProtocolInetIcmp) &&
+ info->iProtocol != STATIC_CAST(TInt, KProtocolInet6Icmp))
+ {
+ switch (info->iVersion)
+ {
+ case 4:
+ iIp->Icmp4Send(packet, KInet4ICMP_Unreachable, 2 /* Protocol Unreach */);
+ return;
+ case 6:
+ iIp->Icmp6Send(packet, KInet6ICMP_ParameterProblem, 1, info->iPrevNextHdr);
+ return;
+ default:
+ break;
+ }
+ }
+ //
+ // Drop silently
+ packet.Free();
+ }
+ void StartSending(CProtocolBase *) {};
+ void Error(TInt, CProtocolBase *) {};
+private:
+ MNetworkService *iIp;
+ };
+
+
+
+//
+// CProtocolPostTerminator
+//
+// CProtocolPostTerminator is purely internal to the ip6.cpp
+// implementation. It is used as a terminal protocol instance
+// for post hook lists. This is *NOT* a visible protocol
+// for the socket manager.
+//
+// *HOWEVER* This is somewhat dubious, drags in some stuff from
+// CProtocolBase. Should use some intermediate class?
+//
+class CProtocolPostTerminator : public CIp6Hook
+ {
+public:
+ CProtocolPostTerminator(CProtocolIP *aIp) : iIp(aIp) {}
+
+ void Process(RMBufChain &aPacket,CProtocolBase *aInterface)
+ {
+ iIp->PostProcess(aPacket, aInterface);
+ }
+
+ TInt Send(RMBufChain &aPacket, CProtocolBase *)
+ {
+ RMBufSendInfo *const info = RMBufSendPacket::PeekInfoInChain(aPacket);
+ if (info)
+ {
+ CFlowContext *const flow = info->iFlow.FlowContext();
+ if (flow)
+ return flow->Send(aPacket);
+ }
+ aPacket.Free();
+ return 1;
+ }
+ TInt ApplyL(RMBufHookPacket &, RMBufRecvInfo &) { return KIp6Hook_PASS; }
+ void StartSending(CProtocolBase *) {}
+ void Error(TInt, CProtocolBase *) {}
+private:
+ CProtocolIP *iIp;
+ };
+
+//
+//
+// Dual IP6/IP4 Stack methods
+// **************************
+// Both IP6 and IP4 have same methods. The method implementations of
+// CProtocol{IP,IP6,IP4) are grouped together, when different implementations
+// are required. Only CProtocolIP method is present when one shared
+// implementations is sufficient.
+//
+//
+// *NOTE*
+// The current configuration is that all of the work is handled
+// by the IPv6 instance (iNetwork), and the IPv4 instance is only
+// required as a connection point to the link layer. However, it
+// is left fully functional in case there is some future need to
+// separate the processing again and have IPv4 and IPv6 with their
+// own iSwitch and hooks. (the current idea is that when hook
+// binds a protocol, it gets both IPv4 and IPv6 for that protocol
+// -- msa
+//
+// Constructors
+// ************
+//
+CProtocolIP::CProtocolIP(CIfManager *aInterfacer, TInt aProtocol) :
+ iInterfacer(aInterfacer),
+ iProtocol(aProtocol),
+ iIcmpProtocol(aProtocol == STATIC_CAST(TInt, KProtocolInet6Ip) ? KProtocolInet6Icmp : KProtocolInetIcmp),
+ iSwitchSize(SwitchSize())
+ {
+ iNetwork = this;
+ }
+
+CProtocolIP6::CProtocolIP6(CIfManager *aInterfacer) : CProtocolIP(aInterfacer, KProtocolInet6Ip)
+ {
+ LOG(Log::Printf(_L("\tip6 new")));
+ }
+
+CProtocolIP4::CProtocolIP4(CIfManager *aInterfacer) : CProtocolIP(aInterfacer, KProtocolInetIp)
+ {
+ LOG(Log::Printf(_L("\tip new")));
+ }
+
+//
+// Destructors
+// ***********
+//
+CProtocolIP::~CProtocolIP()
+ {
+ iNetwork = NULL; // Prevent Close() call from the base class destructor!
+ iSlavedIP4 = NULL; // Not relevant any more!
+
+ while (iForwardFlowSize > 0)
+ iForwardFlow[--iForwardFlowSize].Close(); // Release flow (if allocated)
+ delete[] iForwardFlow;
+ iForwardFlow = NULL;
+
+ iInterfacer->Unregister(this);// No more calls to this from interface manager
+ //
+ // Release all hook lists
+ //
+ CHookEntry *h;
+ for (TUint i = 0; i < iSwitchSize; i++)
+ {
+ // Note: cannot use iSwitch[i].RemoveAll() because the
+ // iNullHook is a member of every list, and it can be
+ // released only once!
+ //
+ while ((h = iSwitch[i].iHead) != iNullHook)
+ {
+ iSwitch[i].iHead = iSwitch[i].iHead->iNext;
+ delete h;
+ }
+ }
+ (void)iOutbound.RemoveAll();
+ (void)iForwardHooks.RemoveAll();
+ (void)iPostInbound.RemoveAll();
+ (void)iPostOutbound.RemoveAll();
+ //
+ // Issue a Close() to all protocols attaced in BindToL
+ //
+ if (iBoundHooks)
+ {
+ for (TInt i = iBoundHooks->Count(); i > 0;)
+ {
+ TBoundHookEntry *bound = &iBoundHooks->At(--i);
+ if (bound->iInterface)
+ {
+ iNifUser->IfUserInterfaceDown(KErrServerTerminated, bound->iInterface);
+ bound->iInterface->Close();
+ }
+ //
+ // "Unbind problem": All protocol classes which are bound
+ // from INET6 must support the Unbind,
+ // [iProtocol cannot be NULL, let crash if so... -- msa]
+ bound->iProtocol->Unbind(this);
+ bound->iProtocol->Close();
+ }
+ delete iBoundHooks;
+ }
+ if (iResolver)
+ iResolver->Close();
+
+ if (iLoopback4)
+ {
+ iNifUser->IfUserInterfaceDown(KErrServerTerminated, iLoopback4);
+ iLoopback4->Close();
+ iLoopback4 = 0;
+ }
+ if (iLoopback6)
+ {
+ iNifUser->IfUserInterfaceDown(KErrServerTerminated, iLoopback6);
+ iLoopback6->Close();
+ iLoopback6 = 0;
+ }
+
+ delete iPostTerminator;
+ delete iFragmentHeader;
+
+ if (iNullHook)
+ {
+ delete iNullHook->iProtocol;
+ delete iNullHook;
+ }
+ }
+
+CProtocolIP6::~CProtocolIP6()
+ {
+ LOG(Log::Printf(_L("\t%S destruct - start"), &ProtocolName()));
+ //
+ // Cleanup
+ //
+ // .. need to do unbinds, because the destructors
+ // of the internal protocols don't do it properly
+ // for now -- msa
+ UnbindAll(iRoutingHeader);
+ UnbindAll(iDestinationOptions);
+ UnbindAll(iHopOptions);
+
+ delete iRoutingHeader;
+ delete iDestinationOptions;
+ delete iHopOptions;
+ LOG(Log::Printf(_L("\t%S destruct - done"), &ProtocolName()));
+ }
+
+CProtocolIP4::~CProtocolIP4()
+ {
+ LOG(Log::Printf(_L("\t%S destruct"), &ProtocolName()));
+ }
+
+// CProtocolIP::HookOrdering
+// *************************
+// Retrieve hook ordering for the specific hooklist.
+// (Currently from TCPIP.INI)
+//
+// Returns a descriptor for the ordering
+// (can be a "NULL" descriptor, if not found)
+//
+TPtrC CProtocolIP::HookOrdering(const TDesC &aOrderKey) const
+ {
+ TPtrC ordering;
+ if (!iInterfacer->FindVar(TCPIP_INI_HOOK, aOrderKey, ordering))
+ return TPtrC(0,0);
+ return ordering;
+ }
+
+//
+// CProtocolIP::BindL
+// ******************
+// (shared by both)
+// The aId has a special format and interpretation:
+//
+// aId >= KIp6Hook_ANY
+// The binding protocol wants to bind "as a hook" for extension
+// header identified by (aid - KIp6Hook_ANY). Such protocol must
+// implement the MExtensionHook interface. If the result after
+// subtract is KIp6Hook_ANY, the request is for generic hook,
+// which is called after all extension headers have
+// been handled, but before passing the packet to the next layer.
+//
+// 0 < aId < KIpHook_ANY
+// The binding protocol want to bind as a normal upper layer protocol
+// for the IP protocol indicated by aId.
+//
+void CProtocolIP::BindL(CProtocolBase *aProtocol, TUint aId)
+ {
+ TServerProtocolDesc info;
+ aProtocol->Identify(&info);
+#ifdef _LOG
+ Log::Printf(_L("BindL\t%S called for %S[%u] id=%d"), &ProtocolName(), &info.iName, (TInt)aProtocol, aId);
+#endif
+
+ if (aId == 0)
+ Panic(EInet6Panic_BadBind); // Cannot bind 0 as protocol!
+ else if (aId == KProtocolInet6Ip && info.iProtocol == KProtocolInet6Ip)
+ {
+ // ip6 bindto ip
+ //
+ // *SPECIAL KLUDGE* -- msa
+ // The current protocol is the "dumb" IPv4 instance and the magic
+ // IPv6 instance is being bound to it: by setting the iNetwork
+ // the IPv4 iSwitch is not used, and all received packets are
+ // handled by the IPv6 iSwitch!
+ // [some typecasting occurring, iNetwork is really
+ // MNetworkService * -- msa]
+ iNetwork = (CProtocolIP *)aProtocol;
+ LOG(Log::Printf(_L("\t\tAssigned %S[%u] as master IPv6, total binds = %d"), &info.iName, (TInt)aProtocol, iBindCount+1));
+ }
+ else if (aId == KProtocolInetIp && info.iProtocol == KProtocolInetIp)
+ {
+ // ip bindto ip6
+ iSlavedIP4 = (CProtocolIP *)aProtocol;
+ LOG(Log::Printf(_L("\t\tAssigned %S[%u] as slaved IPv4, total binds = %d"), &info.iName, (TInt)aProtocol, iBindCount+1));
+ }
+ else if (aId < STATIC_CAST(TUint, KIp6Hook_ANY)) // Note: cannot bind ANY as a protocol!
+ {
+ iSwitch[aId].AddL(aProtocol);
+ LOG(Log::Printf(_L("\t\tBinding %S[%u] as final protocol %d handler, total binds = %d"), &info.iName, (TInt)aProtocol, aId, iBindCount+1));
+ }
+ else if ((aId -= KIp6Hook_ANY) <= STATIC_CAST(TUint, KIp6Hook_ANY))
+ // Note: can bind 0 as a hook!
+ {
+ iBindCount += iSwitch[aId].AddByOrderListL((CIp6Hook *)aProtocol, info.iName, HookOrdering(TCPIP_INI_HOOK_INANY), 1);
+ LOG(Log::Printf(_L("\t\tBinding %S[%u] as protocol %d hook, total binds = %d"), &info.iName, (TInt)aProtocol, aId, iBindCount));
+ return;
+ }
+ else if ((aId -= KIp6Hook_ANY) < STATIC_CAST(TUint, KIp6Hook_ANY))
+ //
+ // Setup as outbound hook with priority = aId (0 < aId < KIpHook_ANY)
+ //
+ {
+ iBindCount += iOutbound.AddByOrderListL((CIp6Hook *)aProtocol, info.iName, HookOrdering(TCPIP_INI_HOOK_FLOW), aId);
+ LOG(Log::Printf(_L("\t\tBinding %S[%u] as outbound flow hook, pri=%d, total binds = %d"), &info.iName, (TInt)aProtocol, aId, iBindCount));
+ return;
+ }
+ else if ((aId -= KIp6Hook_ANY) == 0)
+ {
+ iBindCount += iPostOutbound.AddByOrderListL((CIp6Hook *)aProtocol, info.iName, HookOrdering(TCPIP_INI_HOOK_OUTBOUND), 1);
+ LOG(Log::Printf(_L("\t\tBinding %S[%u] as post outbound hook, total binds = %d"), &info.iName, (TInt)aProtocol, iBindCount));
+ return;
+ }
+ else if (--aId == 0)
+ {
+ iBindCount += iPostInbound.AddByOrderListL((CIp6Hook *)aProtocol, info.iName, HookOrdering(TCPIP_INI_HOOK_INBOUND), 1);
+ LOG(Log::Printf(_L("\t\tBinding %S[%u] as post inbound hook, total binds = %d"), &info.iName, (TInt)aProtocol, iBindCount));
+ return;
+ }
+ else if (--aId == 0)
+ {
+ iBindCount += iForwardHooks.AddByOrderListL((CIp6Hook *)aProtocol, info.iName, HookOrdering(TCPIP_INI_HOOK_FORWARD), 1);
+ LOG(Log::Printf(_L("\t\tBinding %S[%u] as forwarding hook, total binds = %d"), &info.iName, (TInt)aProtocol, iBindCount));
+ return;
+ }
+ else
+ Panic(EInet6Panic_BadBind);
+ iBindCount++;
+ return;
+ }
+
+// CProtocolIP::UnbindAll
+// **********************
+// Remove all bind references to the protocol. This method
+// must be "non-virtual", so that it can be used in
+// destructors.
+void CProtocolIP::UnbindAll(TAny *aProtocol)
+ {
+ // Unbinding IPv4 instance (this must have been "ip bindto ip6" configuration)
+ if (aProtocol == iSlavedIP4)
+ {
+ iSlavedIP4 = NULL;
+ --iBindCount;
+ }
+ for (TUint i = 0; i < SwitchSize(); ++i)
+ iBindCount -= iSwitch[i].Remove(aProtocol);
+ iBindCount -= iOutbound.Remove(aProtocol);
+ iBindCount -= iForwardHooks.Remove(aProtocol);
+ iBindCount -= iPostInbound.Remove(aProtocol);
+ iBindCount -= iPostOutbound.Remove(aProtocol);
+ }
+
+//
+// CProtocolIP::Unbind
+// *******************
+// (shared by both)
+//
+// A protocol wishes not to receive any more packets from the
+// IP level.
+//
+void CProtocolIP::Unbind(CProtocolBase *aProtocol, TUint aId)
+ {
+ LOG(Log::Printf(_L("Unbind\t%S from [%u] id=%d"), &ProtocolName(), (TUint)aProtocol, aId));
+ if (aId == 0)
+ UnbindAll((TAny *)aProtocol);
+ else if (aId < STATIC_CAST(TUint, KIp6Hook_ANY))
+ iBindCount -= iSwitch[aId].Remove(aProtocol);
+ else if ((aId -= KIp6Hook_ANY) <= STATIC_CAST(TUint, KIp6Hook_ANY))
+ iBindCount -= iSwitch[aId].Remove((CIp6Hook *)aProtocol);
+ else if ((aId -= KIp6Hook_ANY) < STATIC_CAST(TUint, KIp6Hook_ANY))
+ iBindCount -= iOutbound.Remove((CIp6Hook *)aProtocol);
+ else if ((aId -= KIp6Hook_ANY) == 0)
+ iBindCount -= iPostOutbound.Remove(aProtocol);
+ else if (--aId == 0)
+ iBindCount -= iPostInbound.Remove(aProtocol);
+ else if (--aId == 0)
+ iBindCount -= iForwardHooks.Remove(aProtocol);
+ //
+ // iNetwork can be NULL, if we are getting here from
+ // ~CPrototolIP()... -- msa
+ //
+ if (iNetwork && aProtocol == iNetwork->Protocol())
+ {
+ --iBindCount;
+ iNetwork = this;
+ }
+ LOG(Log::Printf(_L("Unbind\t%S complete, total binds %d"), &ProtocolName(), iBindCount));
+ }
+
+//
+// CProtocolIP::NewSAPL
+// ********************
+// (shared by both)
+//
+CServProviderBase* CProtocolIP::NewSAPL(TUint aSockType)
+ {
+ return IP6::NewSAPL(aSockType, this, iProtocol);
+ }
+
+
+CProtocolInet6Binder *CProtocolIP::Protocol() const
+ {
+ return (CProtocolInet6Binder *)this;
+ }
+
+MInterfaceManager *CProtocolIP::Interfacer() const
+ {
+ return iInterfacer;
+ }
+
+CHostResolvProvdBase *CProtocolIP::NewHostResolverL()
+ {
+ if (iResolver == NULL)
+ User::Leave(KErrNotSupported);
+ return iResolver->NewHostResolverL();
+ }
+
+CServiceResolvProvdBase *CProtocolIP::NewServiceResolverL()
+ {
+ if (iResolver == NULL)
+ User::Leave(KErrNotSupported);
+ return iResolver->NewServiceResolverL();
+ }
+
+CNetDBProvdBase *CProtocolIP::NewNetDatabaseL()
+ {
+ if (iResolver == NULL)
+ User::Leave(KErrNotSupported);
+ return iResolver->NewNetDatabaseL();
+ }
+
+//
+// CProtocolIP::InitL
+// ******************
+//
+void CProtocolIP::InitL(TDesC& aTag)
+ {
+ TCallBack recvCb(RecvCallBack, this);
+ iRecvQ.InitL(recvCb);
+
+ CProtocolInet6Network::InitL(aTag);
+ //
+ // Setup the protocol switch
+ //
+ CProtocolIP6Null* protIP6Null = new (ELeave) CProtocolIP6Null(this);
+ CleanupStack::PushL(protIP6Null);
+ iNullHook = new (ELeave) CHookEntry(protIP6Null, NULL);
+ CleanupStack::Pop();
+
+ iBindCount = 0;
+ for (TUint i = 0; i < iSwitchSize; i++)
+ iSwitch[i].iHead = iNullHook;
+
+ // Pick up some random start value for the Identification
+ TTime time;
+ TInt64 seed;
+ time.UniversalTime();
+ seed = time.Int64();
+ iId = Math::Rand(seed);
+
+ iPostTerminator = new (ELeave) CProtocolPostTerminator(this);
+ iPostInbound.iChainId = MIp6Hook::BindPostHook()+1;
+ iPostOutbound.iChainId = MIp6Hook::BindPostHook();
+ iPostInbound.AddL(iPostTerminator);
+ iPostOutbound.AddL(iPostTerminator);
+ iFragmentHeader = CFragmentHeaderHook::NewL(this);
+ iFragmentHeader->ConstructL();
+ }
+
+//
+// CProtocolIP6::InitL
+// *******************
+//
+void CProtocolIP6::InitL(TDesC& aTag)
+ {
+ CProtocolIP::InitL(aTag);
+ // Get configured value for the ICMP limiter.
+ iIcmpThrottle.SetMax(iInterfacer->GetIniValue(TCPIP_INI_IP, TCPIP_INI_ICMP_LIMIT, KTcpipIni_IcmpLimit, 0, KMaxTInt));
+ // Enable packet forwarding if allowed by configuration
+ // (only needed for the IP6 instance)
+ iForwardFlowSize = (TUint)iInterfacer->GetIniValue(TCPIP_INI_IP, TCPIP_INI_FORWARD, KTcpipIni_Forward, 0, KMaxTInt);
+ if (iForwardFlowSize > 0)
+ {
+ iForwardFlow = new RFlowContext[iForwardFlowSize];
+ if (iForwardFlow == NULL)
+ {
+ LOG(Log::Printf(_L("No memory for forward = %d, disabled"), (TInt)iForwardFlowSize));
+ iForwardFlowSize = 0;
+ }
+ }
+
+ iRoutingHeader = new (ELeave) CRoutingHeaderHook(this);
+ iRoutingHeader->ConstructL(); // Should call BindL
+
+ iHopOptions = new (ELeave) CHopOptionsHook(this);
+ iHopOptions->ConstructL();
+ iDestinationOptions = new (ELeave) CDestinationOptionsHook(this);
+ iDestinationOptions->ConstructL();
+
+ //Bind myself to handle IPIP-detunneling
+ BindL(this, KProtocolInet6Ipip);
+ // ...need also IPv4 binding here
+ BindL(this, KProtocolInetIpip);
+ iNifUser = iInterfacer->Register(this);
+ }
+
+//
+// CProtocolIP4::InitL
+// *******************
+//
+void CProtocolIP4::InitL(TDesC& aTag)
+ {
+ CProtocolIP::InitL(aTag);
+ iIcmpThrottle.SetMax(iInterfacer->GetIniValue(TCPIP_INI_IP, TCPIP_INI_ICMP_LIMIT, KTcpipIni_IcmpLimit, 0, KMaxTInt));
+ iNifUser = iInterfacer->Register(this);
+ }
+
+//
+// CProtocolIP::BindToL
+// ********************
+// (shared by both)
+//
+void CProtocolIP::BindToL(CProtocolBase *aProtocol)
+ {
+#ifdef _LOG
+ TServerProtocolDesc log_info;
+ aProtocol->Identify(&log_info);
+ Log::Printf(_L("BindToL\t%S binding to %S[%u] start"), &ProtocolName(), &log_info.iName, (TInt)aProtocol);
+#endif
+ //
+ // Experimentally, allow binding of IP instance
+ // to other protocols as a way of activating them
+ // IP instance makes no assumputions about the
+ // protocols bound in this way.
+ //
+ TInt err;
+ aProtocol->Open();
+ TRAP(err, aProtocol->BindL(this, iProtocol));
+ if (err == KErrNone)
+ {
+ TBoundHookEntry binding;
+ binding.iProtocol = (CProtocolBaseUnbind *)aProtocol;
+ binding.iInterface = NULL;
+ if (iBoundHooks || (iBoundHooks = new CArrayFixFlat<TBoundHookEntry>(4)) != NULL)
+ {
+ TServerProtocolDesc info;
+ aProtocol->Identify(&info);
+ if (info.iServiceTypeInfo & EInterface)
+ {
+ // The bound "protocol" is actually some kind of
+ // interface, get the interface and pass it to the
+ // interfacer!
+ TServerProtocolDesc my_info;
+ Identify(&my_info);
+ TRAP(err, binding.iInterface = ((CProtocolInterfaceBase *)aProtocol)->GetBinderL(my_info.iName));
+ if (err == KErrNone && binding.iInterface)
+ {
+ binding.iInterface->Open();
+ TRAP(err, iNifUser->IfUserNewInterfaceL(binding.iInterface, 0));
+ }
+ }
+ if (err == KErrNone)
+ {
+ TRAP(err, iBoundHooks->AppendL(binding));
+ if (err == KErrNone)
+ {
+ //
+ // If all is ok, detect if a "special" protocol providing
+ // the host resolver services is being bound. If so, save
+ // (or replace) a reference also into iResolver.
+ //
+ // [Instead of this, could one just check some bits in
+ // iNamingServices and detect a protocol that way? -- msa
+ //
+ if (info.iProtocol == KProtocolInet6Res)
+ {
+ if (iResolver)
+ iResolver->Close();
+ iResolver = aProtocol;
+ iResolver->Open();
+ }
+ else if (info.iProtocol == KProtocolInetIp)
+ //
+ // this ip6 bindto ip
+ //
+ // iSlavedIP4 is only needed for providing
+ // backward compatibility for old raw sockets
+ // which open to 'ip' protocol and expect to
+ // get IPv4 only with IP header swapped!
+ iSlavedIP4 = (CProtocolIP4 *)aProtocol;
+ else if (info.iProtocol == KProtocolInet6Ip)
+ // this ip bindto ip6
+ iNetwork = (CProtocolIP *)aProtocol;
+ LOG(Log::Printf(_L("BindToL\t%S binding to %S[%u] recorded"), &ProtocolName(), &log_info.iName, (TInt)aProtocol));
+ return; // BindToL completed!
+ }
+ }
+ //
+ // Failed for some reason, cleanup
+ //
+ if (binding.iInterface)
+ {
+ iNifUser->IfUserInterfaceDown(err, binding.iInterface);
+ binding.iInterface->Close();
+ }
+ }
+ else
+ err = KErrNoMemory;
+ // Cancel BindL
+ binding.iProtocol->Unbind(this, iProtocol);
+ }
+ aProtocol->Close();
+#ifdef _LOG
+ Log::Printf(_L("BindToL\t%S binding to %S[%u] failed with %d"), &ProtocolName(), &log_info.iName, (TInt)aProtocol, err);
+#endif
+ User::Leave(err);
+ }
+
+
+//
+// CProtocolIP::StartL
+// ********************
+// (shared)
+//
+void CProtocolIP::StartL(void)
+ {
+ CProtocolInet6Network::StartL();
+
+ if (iProtocol == STATIC_CAST(TInt, KProtocolInet6Ip))
+ {
+ if (iLoopback6 == NULL)
+ {
+ //
+ // Insert IPv6 Loopback Interface and Route
+ //
+ _LIT(loop6, "loop6");
+
+ iLoopback6 = CIfLoop6::NewL(loop6);
+ iLoopback6->Open();
+ iNifUser->IfUserNewInterfaceL(iLoopback6, 0);
+ iInterfacer->AddRouteL(KInet6AddrLoop, 128, loop6, 1);
+ }
+ if (iLoopback4 == NULL)
+ {
+ //
+ // Insert IPv4 Loopback Interface and Route
+ //
+ _LIT(loop4, "loop4");
+
+ iLoopback4 = CIfLoop6::NewL(loop4);
+ iLoopback4->Open();
+ iNifUser->IfUserNewInterfaceL(iLoopback4, 0);
+ TInetAddr loopnet;
+ loopnet.SetV4MappedAddress((127 << 24) + 1);
+ iInterfacer->AddRouteL(loopnet.Ip6Address(),128 - 32 + 8, loop4, 1);
+ }
+ }
+ }
+
+//
+// CProtocolIP::Identify
+// *********************
+
+void CProtocolIP::Identify(TServerProtocolDesc *aInfo) const
+ {
+ IP6::Identify(*aInfo, iProtocol);
+ }
+
+//
+// CProtocolIP::GetOption
+// **********************
+// (shared)
+
+TInt CProtocolIP::GetOption(TUint aLevel, TUint aName, TDes8& aOption, CProtocolBase* /*aSourceProtocol=NULL*/)
+ {
+ if(aLevel == KNifOptLevel)
+ {
+ if(aName == KNifOptGetNifIfUser)
+ {
+ TNifIfUser ifuser;
+ ifuser() = iNifUser;
+ aOption.Copy(ifuser);
+ return KErrNone;
+ }
+ return KErrNotSupported;
+ }
+ return iInterfacer->GetOption(aLevel, aName, aOption);
+ }
+
+//
+// CProtocolIP::SetOption
+// **********************
+// (shared)
+//
+TInt CProtocolIP::SetOption(TUint aLevel, TUint aName, const TDesC8& aOption, CProtocolBase*)
+ {
+ return iInterfacer->SetOption(aLevel, aName, aOption);
+ }
+
+//
+// MergeHeadAndPayload
+// *******************
+// A static, shared utility function for DoBuild methods
+//
+static TInt MergeHeadAndPayload(RMBufPacketBase &aPacket, TPacketHead &aHead, TInt aHdrLen)
+ {
+ TInt err;
+ //
+ // Prepend the head information to the payload part
+ //
+ if (aHead.iOffset > 0)
+ {
+ RMBufChain work;
+ err = aHead.iPacket.Copy(work);
+ if (err == KErrNone)
+ aPacket.Prepend(work);
+ work.Free();
+ }
+ if (aPacket.IsEmpty())
+ err = aPacket.Alloc(aHdrLen);
+ else
+ err = aPacket.Prepend(aHdrLen);
+ return err;
+ }
+
+// DoBuildIp4
+// **********
+static TInt DoBuildIp4(RMBufPacketBase &aPacket, RMBufPktInfo &aInfo, TPacketHead &aHead, TInt aId)
+ {
+ aInfo.iProtocol = KProtocolInetIp;
+ //
+ // Prepend the head information to the payload part
+ //
+ // *NOTE*
+ // The following will create more RMBuf's than
+ // is absolutely necessary. Look into this and
+ // optimize at some point!! -- msa
+ //
+ const TInt ip4len = TInet6HeaderIP4::MinHeaderLength(); // No IP options for now!
+ const TInt err = MergeHeadAndPayload(aPacket, aHead, ip4len);
+ if (err == KErrNone)
+ {
+ TInet6Checksum<TInet6HeaderIP4> ip(aPacket); // Access the packet as IP header.
+ if (ip.iHdr)
+ {
+ aInfo.iLength += ip4len + aHead.iOffset; // ..above merge does not do this!
+ //
+ // Prepare the final packet and info (before hooks).
+ // Note: iOffset does not include the IPv4 header length
+ // Note: err is already KErrNone
+ // Note 3: Certain packets must not have ECN ECT bits set. tcp_sap.cpp sets
+ // KIpNoEcnEct for those packets.
+ ip.iHdr->Init((aInfo.iFlags & KIpNoEcnEct) ?
+ (aHead.ip6.TrafficClass()&0xfc) : aHead.ip6.TrafficClass());
+ ip.iHdr->SetProtocol(aHead.ip6.NextHeader());
+ ip.iHdr->SetTtl(aHead.ip6.HopLimit());
+ // Raw Address load. Someone else must have already checked that
+ // both src and dst are IPv4 mapped addresses at the end of the
+ // ReadyL phase! This just does a blind copy... -- msa
+ ip.iHdr->DstAddrRef() = aHead.ip6.DstAddr().u.iAddr32[3];
+ ip.iHdr->SrcAddrRef() = aHead.ip6.SrcAddr().u.iAddr32[3];
+ // The info->iLength is assumed to be correctly maintained!
+ ip.iHdr->SetTotalLength(aInfo.iLength);
+ ip.iHdr->SetIdentification(aId);
+ //
+ // Somewhat ad hoc thing: if DontFragment flag is set, then
+ // set the DF bit to the IPv4 header... -- msa
+ //
+ if (aInfo.iFlags & KIpDontFragment)
+ // ..this "set" generates more code than is necessary, as
+ // a simple "bitset" to the proper byte would do... --msa
+ ip.iHdr->SetFlags((TUint8)(ip.iHdr->Flags() | KInet4IP_DF));
+ ip.ComputeChecksum();
+ }
+ else
+ return KErrGeneral;
+ }
+ return err;
+ }
+
+// DoBuildIp6
+// **********
+static TInt DoBuildIp6(RMBufPacketBase &aPacket, RMBufPktInfo &aInfo, TPacketHead &aHead)
+ {
+ aInfo.iProtocol = KProtocolInet6Ip;
+ const TInt err = MergeHeadAndPayload(aPacket, aHead, sizeof(TInet6HeaderIP));
+ if (err == KErrNone)
+ {
+ TInet6Packet<TInet6HeaderIP> ip(aPacket); // Access the packet as IP header.
+ if (ip.iHdr)
+ {
+ //
+ // Prepare the final packet and info (before hooks).
+ // Note: iOffset does not include the IPv6 header length
+ // Note: err is already KErrNone!
+ *ip.iHdr = aHead.ip6;
+
+ // Note: Certain packets must not have ECN ECT bits set. tcp_sap.cpp sets
+ // KIpNoEcnEct for those packets.
+ if (aInfo.iFlags & KIpNoEcnEct)
+ {
+ ip.iHdr->SetTrafficClass(ip.iHdr->TrafficClass() & 0xfc);
+ }
+
+ // Note! info iLength is assumed to be correctly maintained!
+ aInfo.iLength += aHead.iOffset; // ..above merge does not do this!
+ ip.iHdr->SetPayloadLength(aInfo.iLength);
+ aInfo.iLength += sizeof(TInet6HeaderIP);
+ }
+ else
+ return KErrGeneral; // Bad packet
+ }
+ return err;
+ }
+
+// CProtocolIP::DoBuild
+// ********************
+// Build packet for IPv4 or IPv6
+//
+TInt CProtocolIP::DoBuild(RMBufPacketBase &aPacket, RMBufPktInfo &aInfo, TPacketHead &aHead)
+ {
+ if (aInfo.iFlags & KIpHeaderIncluded)
+ {
+ // Set aInfo.iProtocol based on the included header
+ TInet6Packet<TInet6HeaderIP4> ip(aPacket); // Access the packet as IP header.
+ if (ip.iHdr)
+ {
+ switch (ip.iHdr->Version())
+ {
+ case 4:
+ aInfo.iProtocol = KProtocolInetIp;
+ return KErrNone;
+ case 6:
+ aInfo.iProtocol = KProtocolInet6Ip;
+ return KErrNone;
+ default:
+ break;
+ }
+ }
+ }
+ else switch (aHead.ip6.Version())
+ {
+ case 4:
+ return DoBuildIp4(aPacket, aInfo, aHead, ++iId);
+ case 6:
+ return DoBuildIp6(aPacket, aInfo, aHead);
+ default:
+ break;
+ }
+ return KErrNotSupported;
+ }
+
+
+// CProtocolIP::Fragment
+// *********************
+TBool CProtocolIP::Fragment(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo, TInt aMtu, RMBufPktQ &aFragments)
+ {
+ if ((aInfo.iFlags & KIpDontFragment) == 0 && iFragmentHeader)
+ {
+ //
+ // Remove the flow context from the packet
+ // (thus fragmenter does not need to worry about it)
+ //
+ RFlowContext orig;
+ orig.Grab(aInfo.iFlow);
+ iFragmentHeader->Fragment(aPacket, aMtu, aFragments);
+ aPacket.Free(); // Just in case...
+ if (aFragments.IsEmpty()) // Fragmentation failed
+ {
+ orig.Close();
+ return EFalse;
+ }
+ //
+ // Attach a reference to the original flow context into each fragment.
+ //
+ TMBufPktQIter iter(aFragments);
+ for (iter.SetToFirst(); iter.More(); iter++)
+ {
+ const RMBufChain& frag = (RMBufChain &)iter.Current();
+ // PeekInfoInChain should really have "const" in parameter...
+ RMBufSendInfo *info = (RMBufSendInfo *)RMBufPacketBase::PeekInfoInChain((RMBufChain &)frag);
+ info->iFlow.Copy(orig);
+ }
+ orig.Close();
+ return ETrue;
+ }
+ else
+ {
+ // Attempt to generate ICMP Error when packet is to be dropped
+ // due unallowed fragmenting
+ aInfo.iFlow.Close(); // Detach flow from info!
+ aPacket.Pack();
+ IcmpWrap(aPacket, TIcmpTypeCode(KInet4ICMP_Unreachable, 4, KInet6ICMP_PacketTooBig, 0), aMtu);
+ ASSERT(aPacket.IsEmpty()); // IcmpWrap is guaranteed to either take the aPacket or to free it.
+ return EFalse;
+ }
+ }
+
+// CProtocolIP::DoSendOnePacket
+// ****************************
+void CProtocolIP::DoSendOnePacket(RMBufSendPacket &aPacket)
+ {
+ RMBufSendInfo *info = aPacket.Unpack();
+ CFlowInternalContext *flow;
+ RMBufPktQ fragments;
+
+ if (!info->iFlow.IsOpen())
+ {
+ //
+ // No flow attached, try opening a flow matching the info.
+ // *WARNING* If anyone (like 6to4) relies on this, remember.. this is a
+ // load to the system (each packet is allocated own flow).
+ // Something better should be done -- msa
+ if (info->iFlow.Open(iNetwork, info->iDstAddr, info->iSrcAddr, info->iProtocol) != KErrNone)
+ goto packet_drop;
+ }
+ flow = (CFlowInternalContext *)info->iFlow.FlowContext(); // Always non-NULL!
+
+ // inilize port information to zero for the NIF
+ // (A hook may change these, if it co-operates with the NIF)
+ info->iSrcAddr.SetPort(0);
+ info->iDstAddr.SetPort(0);
+
+ // Note: DoBuild() initiliazes info->iProtocol either to KProtocolInet6Ip or KProtocolInetIp
+ // depending on the intial IP header (IPv6 or IPv4).
+ //
+ // info->iProtocol can be used by the NIF to decide how the packet will be framed and
+ // it should be properly set.
+ //
+ // If any hook in ApplyHooks adds tunnels, they must also change the the info->iProtocol
+ // to match the new outermost IP header.
+ //
+ if (DoBuild(aPacket, *info, flow->iHead) != KErrNone)
+ goto packet_drop; //
+ if (flow->ApplyHooks(aPacket, *info, fragments, *this) != KErrNone)
+ goto packet_drop;
+ if (fragments.IsEmpty())
+ {
+ aPacket.Pack();
+ iPostOutbound.iHead->iHook->Send(aPacket);
+ }
+ else
+ {
+ // Send out the fragments
+ while (fragments.Remove(aPacket))
+ {
+ iPostOutbound.iHead->iHook->Send(aPacket);
+ }
+ }
+ return;
+packet_drop:
+ if (aPacket.IsEmpty())
+ {
+ // Packet has been "eaten" by someone -- not availble any more
+ LOG(Log::Printf(_L("\tDROPPED SEND -- Packet dropped/processed by hook/internally")));
+ }
+ else
+ {
+ // Default close/free, if packet has not been "eaten" by some already
+#ifdef _LOG
+ TBuf<70> tmp_src;
+ TBuf<70> tmp_dst;
+ TInetAddr::Cast(info->iSrcAddr).OutputWithScope(tmp_src);
+ TInetAddr::Cast(info->iDstAddr).OutputWithScope(tmp_dst);
+ Log::Printf(_L("\tDROPPED SEND src=[%S] dst=[%S] proto=%d"), &tmp_src, &tmp_dst, info->iProtocol);
+#endif
+ info->iFlow.Close(); // Detach flow from packet
+ aPacket.Free(); // Release packet
+ }
+
+ // If packet got fragmented, release the fragments.
+ while (fragments.Remove(aPacket))
+ {
+ info = aPacket.Unpack();
+ info->iFlow.Close();
+ aPacket.Free();
+ }
+ }
+
+// CProtocolIP::InterfaceAttached
+// ******************************
+// Notification from the interface manager about new interface
+// instance being bound to the stack. The network layer does not
+// self need this for anything.
+void CProtocolIP::InterfaceAttached(const TDesC &aName, CNifIfBase *aIf)
+ {
+ // There are two instances, one for IPv6 and one for IPv4.
+ // Currently all processing is concentrated into iNetwork
+ // instance (== IPv6) and the IPv4 instance is just a
+ // "pass-through".
+ if (iNetwork != this && iNetwork)
+ {
+ ((CProtocolIP *)iNetwork->Protocol())->InterfaceAttached(aName, aIf);
+ return;
+ }
+ iOutbound.InterfaceAttached(aName, aIf);
+ iForwardHooks.InterfaceAttached(aName, aIf);
+ iPostOutbound.InterfaceAttached(aName, aIf);
+ iPostInbound.InterfaceAttached(aName, aIf);
+ for (TUint i = 0; i < iSwitchSize; i++)
+ iSwitch[i].InterfaceAttached(aName, aIf);
+ }
+
+// CProtocolIP::InterfaceDetached
+// ******************************
+// Notification from the interface manager about old interface
+// instance being unbound to the stack. The network layer does not
+// self need this for anything.
+void CProtocolIP::InterfaceDetached(const TDesC &aName, CNifIfBase *aIf)
+ {
+ // There are two instances, one for IPv6 and one for IPv4.
+ // Currently all processing is concentrated into iNetwork
+ // instance (== IPv6) and the IPv4 instance is just a
+ // "pass-through".
+ if (iNetwork != this && iNetwork)
+ {
+ ((CProtocolIP *)iNetwork->Protocol())->InterfaceDetached(aName, aIf);
+ return;
+ }
+ iOutbound.InterfaceDetached(aName, aIf);
+ iForwardHooks.InterfaceDetached(aName, aIf);
+ iPostOutbound.InterfaceDetached(aName, aIf);
+ iPostInbound.InterfaceDetached(aName, aIf);
+ for (TUint i = 0; i < iSwitchSize; i++)
+ iSwitch[i].InterfaceDetached(aName, aIf);
+ }
+
+
+//
+// CProtocolIP::StartSending
+// *************************
+// Upcalls from the interfaces
+//
+void CProtocolIP::StartSending(CProtocolBase *aIface)
+ {
+ LOG(Log::Printf(_L("StartSending\t%S for NIF[%u] - Start"), &ProtocolName(), (TInt)aIface));
+
+ // There are two instances, one for IPv6 and one for IPv4.
+ // Currently all processing is concentrated into iNetwork
+ // instance (== IPv6) and the IPv4 instance is just a
+ // "pass-through".
+ if (iNetwork != this && iNetwork)
+ {
+ iNetwork->Protocol()->StartSending(aIface);
+ return;
+ }
+
+ // If the parameter is NULL, then this a forwarded
+ // call from some bound protocol and this only occurs
+ // for UP transitions. Otherwise this is a real StartSending
+ // from the interface and the transition state is decided
+ // by the interface manager.
+ //
+ if (aIface == NULL ||
+ iInterfacer->StartSending((CNifIfBase*)aIface) == KIfaceTransition_UP)
+ {
+ //
+ // Notify All bound prototocols
+ //
+ for (TUint i = 0; i < iSwitchSize; i++)
+ iSwitch[i].StartSending(this);
+ }
+ //
+ // Notify post hook (if any)
+ //
+ if (aIface)
+ // iPostInbound too?
+ iPostOutbound.StartSending(aIface, this);
+ LOG(Log::Printf(_L("StartSending\t%S for NIF[%u] - Done"), &ProtocolName(), (TInt)aIface));
+ }
+
+//
+// CProtocolIP::Error
+// ******************
+// Upcalls from the interfaces
+//
+void CProtocolIP::Error(TInt aError, CProtocolBase* aIface)
+ {
+ LOG(Log::Printf(_L("Error\t%S Error=%d for NIF[%u] - start"), &ProtocolName(), aError, (TInt)aIface));
+
+ // There are two instances, one for IPv6 and one for IPv4.
+ // Currently all processing is concentrated into iNetwork
+ // instance (== IPv6) and the IPv4 instance is just a
+ // "pass-through".
+ if (iNetwork != this && iNetwork)
+ {
+ iNetwork->Protocol()->Error(aError, aIface);
+ return;
+ }
+
+ if (aIface == NULL ||
+ iInterfacer->Error(aError, (CNifIfBase *)aIface) != KIfaceTransition_NONE)
+ {
+ //
+ // Notify All bound prototocols
+ //
+ for (TUint i = 0; i < iSwitchSize; i++)
+ iSwitch[i].Error(aError, this);
+ }
+ //
+ // Notify post hook (if any)
+ //
+ if (aIface)
+ // iPostInbound too?
+ iPostOutbound.Error(aError, aIface, this);
+ LOG(Log::Printf(_L("Error\t%S Error=%d for NIF[%u] - completed"), &ProtocolName(), aError, (TInt)aIface));
+ }
+
+
+#ifdef _LOG
+//
+// VerifyPacket
+// ************
+// This checks the consistency of the various information fields.
+// Intended for DEBUG use to check the correctness of the Hook returns
+//
+static void VerifyPacket(RMBufRecvPacket &aPacket, const RMBufRecvInfo &aInfo)
+ {
+ // - there must be something to process!
+ ASSERT(!aPacket.IsEmpty());
+ // - hook must maintain correct iLength!
+ ASSERT(aInfo.iLength == aPacket.Length());
+ // - the related IP header must always be before the current header
+ ASSERT(aInfo.iOffsetIp < aInfo.iOffset);
+ // - the previous next header field is always before the current header, but after
+ // the related IP header
+ ASSERT(aInfo.iPrevNextHdr > aInfo.iOffsetIp);
+ ASSERT(aInfo.iPrevNextHdr < aInfo.iOffset);
+ // - if the packet is on ICMP error path (IcmpHandler), then iOffsetIp should
+ // point to the IP header of the returned error packet, and must always be
+ // preceded by ICMP header (and thus >= 8)
+ ASSERT(aInfo.iIcmp == 0 || aInfo.iOffsetIp >= 8);
+ // - iOffset is always <= iLength!
+ ASSERT(aInfo.iOffset <= aInfo.iLength);
+ // - legal values for the next header field are [0..255] ( --> iProtocol)
+ ASSERT(aInfo.iProtocol < 256);
+ // - hook can freely mangle the packet RMBufs, but the returned packet
+ // must always have the *original* information block!
+ ASSERT(&aInfo == aPacket.Info());
+ // - info addresses must always be in IPv6 format
+ ASSERT(aInfo.iSrcAddr.Family() == KAfInet6);
+ ASSERT(aInfo.iDstAddr.Family() == KAfInet6);
+ }
+#endif
+
+//
+// ClassifyIcmp
+// ************
+// This is an internal help utility to classify ICMP messages by
+// their type into three categories by following returns
+//
+typedef enum
+ {
+ EIcmpType_NONE, // Not an ICMP, protocol is neither ICMPv4 nor ICMPv6
+ EIcmpType_ECHO, // ICMP echo request
+ EIcmpType_ERROR, // ICMP error report
+ EIcmpType_OTHER // Other ICMP, none of the above
+ } TIcmpType;
+//
+static TIcmpType ClassifyIcmp(const TInt aProtocol, const TInt aType)
+ {
+ if (aProtocol == STATIC_CAST(TInt, KProtocolInet6Icmp))
+ {
+ if (aType == KInet6ICMP_EchoRequest) // .. == 128
+ return EIcmpType_ECHO;
+ else if (aType > KInet6ICMP_EchoRequest) // .. > 128
+ return EIcmpType_OTHER; // ...are non-Error ICMP's
+ else
+ return EIcmpType_ERROR;
+ }
+ else if (aProtocol == STATIC_CAST(TInt, KProtocolInetIcmp))
+ {
+ switch (aType)
+ {
+ case KInet4ICMP_Unreachable: // Destination unreachable
+ case KInet4ICMP_SourceQuench: // Source Quench
+ case KInet4ICMP_Redirect: // Redirect request
+ case KInet4ICMP_TimeExceeded: // Time Exceeded
+ case KInet4ICMP_ParameterProblem: // Parameter Problem
+ return EIcmpType_ERROR;
+ case KInet4ICMP_Echo: // Echo
+ return EIcmpType_ECHO;
+ default:
+ return EIcmpType_OTHER;
+ }
+ }
+ return EIcmpType_NONE;
+ }
+
+//
+// Compute simple hash value from TInetAddr
+//
+static TUint HashIt(const TInetAddr &aAddr)
+ {
+ TUint hash = aAddr.Port() & 0xffff;
+ const TIp6Addr &addr = aAddr.Ip6Address();
+ hash += addr.u.iAddr16[0];
+ hash += addr.u.iAddr16[1];
+ hash += addr.u.iAddr16[2];
+ hash += addr.u.iAddr16[3];
+ hash += addr.u.iAddr16[4];
+ hash += addr.u.iAddr16[5];
+ hash += addr.u.iAddr16[6];
+ hash += addr.u.iAddr16[7];
+ hash += aAddr.Scope();
+ return hash;
+ }
+
+
+
+// CProtocolIP::SetHookValue
+// *************************
+TInt CProtocolIP::SetHookValue(const TUint32 aId, const TUint32 aValue)
+ {
+ // The packet id (identified iwth aId==0) cannot be set (read-only)
+ if (aId == 0)
+ return KErrArgument;
+
+ TInt j = iPacketContextCount; // Next free slot, if any
+ for (TInt i = 0;; ++i)
+ {
+ if (i == iPacketContextCount)
+ {
+ // Key not found.
+ if (aValue == 0)
+ return KErrNone; // No need to store 0 values!
+ if (j == i) // == iPacketContextCount
+ {
+ // need to allocate new slot
+ const TInt size = sizeof(iPacketContext) / sizeof(iPacketContext[0]);
+ if (j == size)
+ return KErrNoMemory; // No room!
+ iPacketContextCount += 1;
+ }
+ break;
+ }
+ if (iPacketContext[i].iKey == aId)
+ {
+ j = i; // Same key, replace with new value
+ break;
+ }
+ if (iPacketContext[i].iValue == 0)
+ j = i; // If value == 0, then entry can be reused
+ }
+
+ iPacketContext[j].iKey = aId;
+ iPacketContext[j].iValue = aValue;
+ return KErrNone;
+ }
+
+// CProtocolIP::HookValue
+// **********************
+TUint32 CProtocolIP::HookValue(const TUint32 aId) const
+ {
+ // aId==0 is a special key for the Packet Id (read-only)
+
+ if (aId == 0)
+ return iPacketId;
+
+ for (TInt i = 0; i < iPacketContextCount; ++i)
+ if (iPacketContext[i].iKey == aId)
+ return iPacketContext[i].iValue;
+ return 0;
+ }
+
+//
+// CProtocolIP::DoSwitchL()
+// ************************
+//
+TBool CProtocolIP::DoSwitchL(RMBufHookPacket &aPacket)
+ /**
+ * Processes one layer of IP header with extension headers.
+ *
+ * @param aPacket The packet (unpacked state)
+ *
+ * @return
+ * @li FALSE, if packet has been processed (either dropped or accepted)
+ * @li TRUE, if detunneling happened (packet contained another IP header)
+ *
+ * The leave happens if a called ApplyL leaves.
+ * The caller must trap the leave and release the packet.
+ */
+ {
+packet_redo:
+
+ // If a generic hook modifies the packet (returns KIp6Hook_DONE), then
+ // this cancels the currently selected transport protocol (target) and
+ // the protocol specific hooks for the new protocol are executed. After
+ // this, all generic hooks are executed again.
+ //
+ // If none of the protocol specific hooks modifies the packet (all return
+ // KIp6Hook_PASS), then without the exclusion mechanism, the original
+ // generic hook, which restarted the process, would be called again with
+ // exactly the same packet for which it already reported DONE. To spare
+ // the hook from responsibility of detecting this situation, the
+ // exclusion is implemented here (exclude_this), and the protocol is
+ // not called.
+ CHookEntry *exclude_this = NULL;
+
+ CHookEntry *h;
+ TInt ret = 0;
+ RMBufRecvInfo *const info = aPacket.Info();
+ info->iFlags &= ~KIpAddressVerified; // Verify addressess at least at beginning!
+ for (;;)
+ {
+ //
+ // *NOTE*
+ // If iProtocol == 0, this is a Hob-by-Hop header. To get it executed, skip
+ // the address check and fall to the switch loop without clearing the
+ // address flag. After header is handled and skipped, the control comes back
+ // here and address is now checked. (This can happen only initially, HBH is
+ // is not accepted from the hooks at later stages)
+ //
+ if (info->iProtocol && (info->iFlags & KIpAddressVerified) == 0)
+ {
+ info->iFlags |= KIpAddressVerified; // .. it will be after following.
+
+ if (info->iVersion == 4)
+ {
+ //
+ // IPv4 specific address tests (for now, just blindly assume
+ // that addresses are in IPv4 mapped format, and fetch the last
+ // 4 bytes of the IPv6 address... (NETWORK ORDER!)
+ //
+ const TUint32 src32 = TInetAddr::Cast(info->iSrcAddr).Ip6Address().u.iAddr32[3];
+
+ if (((TUint8 &)src32) >= 224) // Do not accept multicast or other weirdos as src.
+ goto packet_drop;
+ }
+ else
+ {
+ // IPv6 specific address tests
+ //
+ // Note: ND Duplicate Address Detection requires that src=Unspecified must pass through..
+ if (TIp46Addr::Cast(TInetAddr::Cast(info->iSrcAddr).Ip6Address()).IsMulticast())
+ goto packet_drop;//Do not accept Multicast as a source address!
+ }
+
+ ret = iInterfacer->IsForMePacket(*info);
+ if (ret < 0)
+ goto packet_drop;
+ else if (ret == 0)
+ break; // Not for me, forward?
+ }
+ //
+ //
+ // Call Extension Header Handlers and Hooks
+ //
+ CProtocolBase *target = NULL;
+ for (h = iSwitch[info->iProtocol].iHead;;)
+ {
+ ASSERT(h != NULL); // ..there is ALWAYS the NULL protocol with IsProtocol() == TRUE!
+ if (info->iOffset > info->iLength)
+ goto packet_drop;
+
+ if (h->IsProtocol())
+ {
+ if (target)
+ {
+ if (target == this)
+ {
+ //
+ // Detunneling detected. IP is registered as upper layer
+ // only for IP-in-IP tunneling protocols.
+ //
+ aPacket.TrimStart(info->iOffset); // Trim to the inner IP header.
+ info->iOffset = 0;
+ return TRUE;
+ }
+
+ if (aPacket.IsEmpty())
+ goto packet_drop; // Cannot have EMPTY packet at this stage!
+
+ ASSERT(info->iLength == aPacket.Length());
+ //
+ // Deliver packets to raw SAP's (if any). Demux packets based
+ // on IP version to appropriate IP protocol instance (if we
+ // have the IP6 + IP4slave configuration).
+ //
+ if (info->iVersion == 4 && iSlavedIP4)
+ {
+ if (iSlavedIP4->iSapCount > 0)
+ iSlavedIP4->Deliver(aPacket);
+ }
+ else if (iSapCount > 0)
+ Deliver(aPacket);
+
+ if (info->iProtocol == STATIC_CAST(TInt, KProtocolInet6Icmp) || info->iProtocol == STATIC_CAST(TInt, KProtocolInetIcmp))
+ {
+ // Call internal ICMP handler
+ if (IcmpHandlerL(aPacket, *info, target) < 0)
+ goto packet_done;
+ }
+ aPacket.Pack();
+ target->Process(aPacket);
+ goto packet_done;
+ }
+ //
+ // Target protocol found (Phase 1 done)
+ //
+ target = h->iProtocol; // Always != NULL!!
+ //
+ // Now, process all generic hook(s), if any registered
+ //
+ h = iSwitch[KIp6Hook_ANY].iHead;
+ }
+ else
+ {
+ if (h != exclude_this)
+ {
+ ret = h->iHook->ApplyL(aPacket, *info);
+ if (ret < 0)
+ goto packet_done;
+ LOG(VerifyPacket(aPacket, *info)); // Hook sanity checks...
+ if (ret == KIp6Hook_DONE)
+ {
+ if (target)
+ exclude_this = h; // -- prevent repeated call for the same packet
+ else
+ exclude_this = NULL; // -- remove exclusion (if any)
+ if (info->iProtocol == 0)
+ {
+ // HOP-by-HOP options can only appear as first after IP header
+ // ?What about IPv4 case? -- msa
+ Icmp6Send(aPacket, KInet6ICMP_ParameterProblem, /*code*/ 1, info->iPrevNextHdr);
+ goto packet_done;
+ }
+ break; // == Next header (need to recheck MyAddress!)
+ }
+ ASSERT(ret == KIp6Hook_PASS);
+ }
+ h = h->iNext;
+ }
+ }
+ }
+#ifdef SYMBIAN_TCPIPDHCP_UPDATE
+ //Check whether the source address scope is less than the destination address scope,if that is the case through
+ //ICMP error code 2,beyond scope of source address (Refer RFC 4443: sec 3.1)
+ if(info->iVersion == 6)
+ {
+ TInet6Checksum<TInet6HeaderIP4> ip(aPacket, info->iOffsetIp);
+ if (!ip.iHdr)
+ {
+ goto packet_drop;
+ }
+ TInet6HeaderIP *const ip6 = (TInet6HeaderIP *)ip.iHdr;
+ if (ip.iLength < TInet6HeaderIP::MinHeaderLength())
+ {
+ goto packet_drop;
+ }
+ if ( ip6->SrcAddr().Scope() < ip6->DstAddr().Scope())
+ {
+ // Attempting to forward out of scope of the source address (mainly for IPv6)
+ aPacket.Pack();
+ IcmpWrap(aPacket, TIcmpTypeCode(KInet4ICMP_Unreachable, 0, KInet6ICMP_Unreachable, 2));
+ return EFalse;
+ }
+ }//end of scope check
+#endif // SYMBIAN_TCPIPDHCP_UPDATE
+ //
+ // *************************
+ // Packet forwarding section
+ // *************************
+ // (a simple and quick sketch, not efficient, including
+ // juggling of addresses between flow and info... -- msa)
+ //
+
+ // Try forwarding hooks first
+ //
+ for (h = iForwardHooks.iHead; h != NULL; h = h->iNext)
+ {
+ ret = h->iHook->ApplyL(aPacket, *info);
+ if (ret < 0)
+ goto packet_done;
+
+ LOG(VerifyPacket(aPacket, *info)); // Hook sanity checks...
+ if (ret == KIp6Hook_DONE)
+ goto packet_redo;
+ ASSERT(ret == KIp6Hook_PASS);
+ }
+
+ if (info->iIcmp == 0 && iForwardFlowSize > 0)
+ {
+ if (TInetAddr::Cast(info->iDstAddr).IsMulticast())
+ goto packet_drop; // Do not forward multicast packets!
+ //
+ // Modify some of IP header parameters before attempting to forward
+ // (specifically addresses which may have been changed due to routing
+ // header and other similar processing)
+ //
+ TInt hops;
+ info->iFlags = KIpDontFragment|KIpHeaderIncluded;
+ TInet6Checksum<TInet6HeaderIP4> ip(aPacket, info->iOffsetIp);
+ if (!ip.iHdr)
+ goto packet_drop;
+
+ if (info->iVersion == 4)
+ {
+ // IPv4 specific forwarding checks
+ hops = ip.iHdr->Ttl() - 1;
+ ip.iHdr->SetTtl(hops); // Blindly set, don't care if 0 or negative.
+ ip.ComputeChecksum();
+ if (!ip.iHdr->DF())
+ info->iFlags &= ~KIpDontFragment; // Allow fragmenting
+ }
+ else
+ {
+ // IPv6 specific forwarding checks
+ // IPv4 mapping will cover also all of the IPv6 header, if packet is long enough
+ TInet6HeaderIP *const ip6 = (TInet6HeaderIP *)ip.iHdr;
+ if (ip.iLength < TInet6HeaderIP::MinHeaderLength())
+ goto packet_drop;
+
+ hops = ip6->HopLimit() - 1;
+ ip6->SetHopLimit(hops); // Blindly set, don't care if 0 or negative.
+ // For now, IPv6 only forwards unicast addresses (basicly need to drop
+ // all routers addresseses...
+ if (!ip6->DstAddr().IsUnicast())
+ goto packet_drop;
+ }
+ //
+ // For the flow, do some quick upper layer snooping
+ // (also needed to detect ICMP errors and not reply to them)
+ // *NOTE*
+ // This snoop is intentionally "shallow", only the outermost
+ // layer is tested. If an ICMP error is hidden under some
+ // extension headers, it is perfectly legal to reply to it
+ // with Time Exceeded (as it is in general case impossible
+ // to skip over the potentially unkown extension headers
+ // here!)
+ // *NOTE*
+ // All of this does not make much sense, because it is never
+ // 100% sure for IPSEC. Thus, the right thing is not to try!
+ // - only do this lightweight snoop for ICMP when Time
+ // exceeded is about to be sent (its optional in IPv6 anyway)
+ // - accept that any IPSEC policies which affect forwarding,
+ // can only be written based on src/dst addresses. The protocol
+ // can be used, but with knowledge, that it will always be
+ // the outermost header just below the IP header! -- msa
+ info->iSrcAddr.SetPort(0);
+ info->iDstAddr.SetPort(0);
+ // (borrow the info type & code fields for a moment)
+ info->iType = 0;
+ info->iCode = 0;
+ {
+ const TUint proto = (TUint)info->iProtocol;
+ const TInt is_icmp = proto == KProtocolInetIcmp || proto == KProtocolInet6Icmp;
+ const TInt is_ports = proto == KProtocolInetUdp || proto == KProtocolInetTcp;
+
+ if (is_icmp || is_ports)
+ {
+ TInet6Packet<TUpperLayerSnoop> snoop(aPacket, info->iOffset);
+ if (snoop.iHdr == NULL) // runt packet!
+ goto packet_drop;
+ if (is_icmp)
+ {
+ info->iType = snoop.iHdr->icmp.Type();
+ info->iCode = snoop.iHdr->icmp.Code();
+ }
+ else
+ {
+ info->iDstAddr.SetPort(snoop.iHdr->udp.DstPort());
+ info->iSrcAddr.SetPort(snoop.iHdr->udp.SrcPort());
+ }
+ }
+ }
+#ifdef _LOG
+ {
+ TBuf<70> tmp_src;
+ TBuf<70> tmp_dst;
+ TInetAddr::Cast(info->iSrcAddr).OutputWithScope(tmp_src);
+ TInetAddr::Cast(info->iDstAddr).OutputWithScope(tmp_dst);
+ Log::Printf(_L("\tFORWARDING src=[%S %d] dst=[%S %d] proto=%d type=%d code=%d hops=%d (%d hit=%u miss=%u)"),
+ &tmp_src, info->iSrcAddr.Port(),
+ &tmp_dst, info->iDstAddr.Port(),
+ info->iProtocol,
+ (TInt)info->iType, (TInt)info->iCode,
+ hops,
+ iForwardFlowCount,
+ iForwardHits,
+ iForwardMiss);
+ }
+#endif
+ if (hops <= 0)
+ {
+ //
+ // Complain with Time Exceeded ICMP error
+ //
+ // NOTE:
+ // ClassifyIcmp can be called with non-ICMP protocol, it only returns
+ // EIcmpType_ERROR, if there is a real ICMP protocol!
+ //
+ if (ClassifyIcmp(info->iProtocol, info->iType) == EIcmpType_ERROR)
+ goto packet_drop; // Avoid sending ICMP error for ICMP Error!
+ if (info->iVersion == 4)
+ Icmp4Send(aPacket, KInet4ICMP_TimeExceeded, /*code*/ 0, /*Paramater*/ 0);
+ else
+ Icmp6Send(aPacket, KInet6ICMP_TimeExceeded, /*code*/ 0, /*Paramater*/ 0);
+ goto packet_done;
+ }
+ //
+ // Choose the flow
+ //
+ const TUint hash =
+ HashIt(TInetAddr::Cast(info->iDstAddr)) +
+ HashIt(TInetAddr::Cast(info->iSrcAddr)) +
+ info->iCode +
+ info->iType +
+ info->iProtocol +
+ info->iInterfaceIndex;
+ RFlowContext &flow = iForwardFlow[hash % iForwardFlowSize];
+ if (!flow.IsOpen())
+ {
+ if (flow.Open(this, 0) != KErrNone)
+ goto packet_drop; // No flow context and creation failed, just drop.
+ // Mark this as forwarding flow.
+ flow.FlowContext()->iInfo.iForwardingFlow = 1;
+ // Do not keep interfaces up due to forwarding flows.
+ flow.FlowContext()->SetOption(KSolInetIp, KSoKeepInterfaceUp, KInetOptionDisable);
+ iForwardFlowCount += 1;
+ }
+ //
+ // Set up flow for the packet
+ //
+ flow.SetRemoteAddr(info->iDstAddr);
+ flow.SetLocalAddr(info->iSrcAddr);
+ flow.SetProtocol(info->iProtocol);
+ flow.SetIcmpType(info->iType, info->iCode);
+ //
+ // Collect some statistics
+ //
+ if (((CFlowInternalContext *)flow.FlowContext())->IsChanged())
+ iForwardMiss += 1;
+ else
+ iForwardHits += 1;
+
+ ret = ((RMBufSendInfo *)info)->iFlow.Open(flow);
+ if (ret == KErrNone)
+ {
+ if (info->iOffsetIp > 0) // Throw away outer IP header layers (in case some detunneling left them in buffer)
+ aPacket.TrimStart(info->iOffsetIp); // (info->iLength updated by TrimStart!)
+ aPacket.Pack();
+ Send(aPacket);
+ return FALSE;
+ }
+ else if (ret == KErrInet6SourceAddress)
+ {
+ // Attempting to forward out of scope of the source address (mainly for IPv6)
+ aPacket.Pack();
+ IcmpWrap(aPacket, TIcmpTypeCode(KInet4ICMP_Unreachable, 0, KInet6ICMP_Unreachable, 2));
+ return FALSE;
+ }
+ }
+ // Just FALL to packet drop on flow attach failure
+packet_drop:
+#ifdef _LOG
+ {
+ TBuf<70> tmp_src;
+ TBuf<70> tmp_dst;
+ TInetAddr::Cast(info->iSrcAddr).OutputWithScope(tmp_src);
+ TInetAddr::Cast(info->iDstAddr).OutputWithScope(tmp_dst);
+ Log::Printf(_L("\tDROPPING src=[%S] dst=[%S] proto=%d"), &tmp_src, &tmp_dst, info->iProtocol);
+ }
+#endif
+ aPacket.Free();
+packet_done:
+ ASSERT(aPacket.IsEmpty());
+ return FALSE;
+ }
+
+//
+// CProtocolIP::DoProcessOnePacketL
+// ********************************
+// The same code is used for both IPv6 and IPv4 protocol instances and
+// this accepts both type of packets, regardless of the source (it accepts
+// IPv6 packets from IPv4 interface and vice versa).
+//
+// The caller must trap the leave and release the packet!
+// The caller is also assumed to free the packet if not consumed by this.
+//
+void CProtocolIP::DoProcessOnePacketL(RMBufHookPacket &aPacket)
+ {
+ RMBufRecvInfo *const info = aPacket.Unpack();
+
+#ifdef ARP
+ //
+ // Assume the packet contains an IPv4 ARP packet, if the
+ // iProtocol is set to KProtocolArp.
+ //
+ if (info->iProtocol == STATIC_CAST(TInt, KProtocolArp))
+ {
+ (void)iInterfacer->ArpHandler(aPacket, *info);
+ return;
+ }
+#endif
+
+ // Bump the packet id, Zero is not accepted.
+ if (++iPacketId == 0)
+ iPacketId = 1;
+
+ // RFC-3168 detunneling: when a packet arrives, there
+ // is no outer header and the congestion flas is
+ // initially OFF.
+ TBool ecnCongestion = EFalse;
+
+ do {
+ // Each IP header resets the packet context (when detunneling is
+ // done, the previous context data is forgotten. This is needed
+ // because the main use of the context is to detect options
+ // implemented by hooks, and if a new IP layer is started, the
+ // previous option data is not any more relevant).
+ //
+ // Note: The packet id is not changed when detunneling occurs.
+ //
+ iPacketContextCount = 0; // Reset Packet Context
+
+ TInet6Packet<TIpHeader> ip(aPacket);
+ TInt total, hlen;
+
+ // RFC-3168: This a temporary copy of the TOS (IPv4) or
+ // Traffic Class (IPv6) from the current IP header.
+ TInt tos;
+
+ if (!ip.iHdr)
+ break; // Bad packet, just drop
+
+ info->iOffsetIp = 0; // First IP header is at start of the buffer.
+ info->iIcmp = 0;
+ info->iVersion = (TUint8)ip.iHdr->ip4.Version();
+ if (info->iVersion == 4)
+ {
+ hlen = ip.iHdr->ip4.HeaderLength();
+ if (hlen < TInet6HeaderIP4::MinHeaderLength())
+ break; // Corrupt IPv4 packet (header too short!)
+ total = ip.iHdr->ip4.TotalLength();
+ info->iPrevNextHdr = TInet6HeaderIP4::O_Protocol;
+ tos = ip.iHdr->ip4.TOS();
+ }
+ else if (info->iVersion == 6)
+ {
+ hlen = ip.iHdr->ip6.HeaderLength();
+ total = hlen + ip.iHdr->ip6.PayloadLength();
+ info->iPrevNextHdr = TInet6HeaderIP::O_NextHeader;
+ tos = ip.iHdr->ip6.TrafficClass();
+ }
+ else
+ break; // Only IPv4 and IPv6 are supported (probably corrupt packet)
+
+ if (ip.iLength < hlen)
+ break; // The packet is not long enough for IPv4 or IPv6 header
+
+ info->iOffset = hlen;
+
+ const TInt extra = info->iLength - total;
+ if (extra < 0)
+ break; // The packet is not long enough for the indicated payload
+ else if (extra > 0)
+ {
+ info->iLength -= extra;
+ aPacket.TrimEnd(info->iLength); // (note, info->iLength also reset by TrimEnd!)
+ }
+
+ if (info->iFlags & KIpNoEcnEct || (tos & 3) == 0)
+ {
+ // ECN has been disabled for this packet/header. Clear out
+ // congestion status (if set).
+ ecnCongestion = EFalse;
+ }
+ else
+ {
+ // Current IP has ECN enabled.
+ ecnCongestion |= (tos & 3) == 3; // Set ecnCongestion if CE flag.
+ // Proactively, set CE to the saved TOS/TrafficClass. It will only be
+ // copied to the packet header, if ecnCongestion is also set.
+ tos |= 3;
+ }
+ if (info->iVersion == 4)
+ {
+ if (TChecksum::ComplementedFold(TChecksum::Calculate((TUint16*)ip.iHdr, hlen)) != 0)
+ break; // Packet fails IPv4 header checksum
+ if (ecnCongestion)
+ {
+ // Set ECN CE to inner header if ECN is enabled and
+ // if outer header indicated congestion. Note: IP checksum
+ // is not updated (The assumption is that the checksum is not
+ // checked after this by anyone, and if the packet ends up
+ // being forwarded, the checksum is recomputed there anyway).
+ ip.iHdr->ip4.SetTOS(tos);
+ }
+ // Setup addresses and protocol into info before the
+ // fragment assembly (if this is the first fragment, the
+ // values will be the final values for the assembled
+ // packet).
+ TInetAddr::Cast(info->iSrcAddr).SetV4MappedAddress(ip.iHdr->ip4.SrcAddr());
+ TInetAddr::Cast(info->iDstAddr).SetV4MappedAddress(ip.iHdr->ip4.DstAddr());
+ info->iProtocol = ip.iHdr->ip4.Protocol();
+ if (ip.iHdr->ip4.MF() || ip.iHdr->ip4.FragmentOffset())
+ {
+ if (iFragmentHeader->Ip4ApplyL(aPacket, *info, ip.iHdr->ip4) < 0)
+ break;
+ LOG(VerifyPacket(aPacket, *info));
+ }
+ }
+ else
+ {
+ if (ecnCongestion)
+ {
+ // Set ECN CE to inner header if ECN is enabled and
+ // if outer header indicated congestion.
+ ip.iHdr->ip6.SetTrafficClass(tos);
+ }
+ TInetAddr::Cast(info->iSrcAddr).SetAddress(ip.iHdr->ip6.SrcAddr());
+ TInetAddr::Cast(info->iSrcAddr).SetFlowLabel(ip.iHdr->ip6.FlowLabel());
+ TInetAddr::Cast(info->iDstAddr).SetAddress(ip.iHdr->ip6.DstAddr());
+ TInetAddr::Cast(info->iDstAddr).SetFlowLabel(0);
+ info->iProtocol = ip.iHdr->ip6.NextHeader();
+ }
+ //
+ // Dispatch the packet (returns TRUE, only if tunneled IP header is uncovered)
+ //
+ } while (DoSwitchL(aPacket));
+ }
+
+//
+// CProtocolIP::DoProcess
+// **********************
+// The same code is used for both IPv6 and IPv4 protocol instances and
+// this accepts both type of packets, regardless of the source (it accepts
+// IPv6 packets from IPv4 interface and vice versa).
+//
+void CProtocolIP::DoProcess()
+ {
+ ASSERT(this == iNetwork);
+
+ RMBufHookPacket packet(this);
+ LOG(Log::Printf(_L("--- Process Queued Packets")));
+ while (iRecvQ.Remove(packet))
+ {
+ //
+ // Process one IP packet
+ //
+ TRAPD(err, DoProcessOnePacketL(packet));
+ err = err; // Clearing "never used" warning caused by TRAPD
+ //
+ // Free leftovers, if any
+ //
+ LOG(if (!packet.IsEmpty()) Log::Printf(_L("\tPacket dropped by LEAVE %d"), err););
+ packet.Free();
+ }
+ LOG(Log::Printf(_L("--- End Processing")));
+ }
+
+//
+// CProtocolIP::Process
+// *********************
+// The same code is used for both IPv6 and IPv4 protocol instances and
+// this accepts both type of packets, regardless of the source (it accepts
+// IPv6 packets from IPv4 interface and vice versa).
+//
+// However, the hook lists are valid only for 'ip6' instance. After this,
+// all continued process occurs under the 'ip6' instance!
+//
+void CProtocolIP::Process(RMBufChain &aPacket, CProtocolBase* aInterface)
+ {
+ LOG(PktLog(_L("--- Packet from NIF[%u]%S: prot=%d src=%S dst=%S len=%d [calling prehooks]"), *RMBufPacketBase::PeekInfoInChain(aPacket), (TUint)aInterface, _L("")));
+ // This byte count updating does not really belong to IP stack. It should
+ // be done within NIF to be absolutely accurate... -- msa
+ CNifIfBase *const nif = (CNifIfBase *)aInterface;
+ if (nif && nif->Notify())
+ {
+ const RMBufRecvInfo *const info = RMBufRecvPacket::PeekInfoInChain(aPacket);
+ if (info)
+ (void)nif->Notify()->PacketActivity(EIncoming, (TUint)info->iLength, FALSE);
+ }
+ // Always shunt the remaining processing to the real ip6 instance.
+ ((CProtocolIP*)iNetwork)->iPostInbound.iHead->iHook->Process(aPacket, aInterface);
+ }
+
+// CProtocolIP::PostProcess
+// ************************
+//
+void CProtocolIP::PostProcess(RMBufChain &aPacket, CProtocolBase *aInterface)
+ {
+ ASSERT(this == iNetwork);
+
+ RMBufRecvInfo *const info = RMBufRecvPacket::PeekInfoInChain(aPacket);
+
+ const MInterface *const mi = iInterfacer->Interface((CNifIfBase *)aInterface);
+ if (mi)
+ {
+ LOG(PktLog(_L("--- Packet after prehooks IF %u [%S] prot=%d src=%S dst=%S len=%d [Queued]"), *info, mi->Index(), mi->Name()));
+ info->iInterfaceIndex = info->iOriginalIndex = mi->Index();
+ // Insert the packet into a queue and request a callback. This is to
+ // avoid extremely deep calling stacks. Up to this point, the processing
+ // has been under the "RunL" of some NIF or driver. This queueing
+ // "break" ensures that the actual IP processing of the packet starts
+ // with a "fresh and direct" call from active scheduler.
+ iRecvQ.Append(aPacket);
+ iRecvQ.Wake();
+ }
+ else
+ {
+ LOG(PktLog(_L("--- Packet after prehooks Unknown NIF[%u]%S prot=%d src=%S dst=%S len=%d [Dropped]"), *info, (TUint)aInterface, _L("")));
+ aPacket.Free(); // Unknown interface, drop silently!
+ }
+ }
+
+//
+// Recv callback handler. Called to kick off handling of
+// any datagrams on our recv queue.
+//
+TInt CProtocolIP::RecvCallBack(TAny* aProtocol)
+ {
+ ((CProtocolIP*)aProtocol)->DoProcess();
+ return 0;
+ }
+
+//
+// CProtocolIP::Send
+// *****************
+//
+TInt CProtocolIP::Send(RMBufChain& aPacket,CProtocolBase * /* aSrc */)
+ {
+ RMBufSendPacket packet;
+ packet.Assign(aPacket);
+ DoSendOnePacket(packet);
+ return 1;
+ }
+
+
+//
+// ************************
+// Internal ICMP processing
+// ************************
+// The ICMP is an essential part of the IP stack and part of the ICMP processing
+// is mandatory. For this reason, the ICMP handling is implemented as a part of
+// the IP level implementation here.
+//
+// Another reason is that if all of the ICMP is implemented as a separate
+// protocol module, it appears to create some complexities, because IP needs
+// ICMP and ICMP needs IP, thus the natural BIND directives would form a
+// loop. However, making a loop that way would prevent SocketServer from
+// terminating. The current solution is as follows:
+//
+// - Normal ICMP protocol is provided for applications, such as PING.
+// This is bound to the IP level normally. However, this protocol is
+// just a "gateway" for ICMP packets for applications.
+//
+// - Most of the mandatory ICMP functionality is implemented here as
+// a part of the IP protocol instance.
+//
+//
+// CProtocolIP::IcmpEcho
+// *********************
+// Generate ICMP echo reply to either IPv4 or IPv6 depending on aProtocol
+//
+void CProtocolIP::IcmpEcho(RMBufPacketBase &aPacket, RMBufRecvInfo *aInfo)
+ {
+ RMBufSendPacket packet;
+ RMBufSendInfo *info;
+
+ LOG(Log::Printf(_L("\tIcmpEcho(%d bytes)"), aInfo->iLength));
+ packet.SetInfo((RMBufSendInfo *)aInfo);
+ aPacket.SetInfo(NULL);
+ packet.Assign(aPacket);
+ info = packet.Info();
+
+ //
+ // Discard IP headers upto ICMP header
+ //
+ packet.TrimStart(aInfo->iOffset);
+ //
+ // The packet already has ICMP Echo Request header, so just map it
+ //
+ TInet6Checksum<TInet6HeaderICMP_Echo> echo(packet);
+ if (echo.iHdr && !iIcmpThrottle.Suppress())
+// if (echo.iHdr)
+ {
+ // Swap src and dest addresses
+ info->iSrcAddr.Swap(aInfo->iDstAddr);
+ // If the request destination was not my own assigned address, then do
+ // not use it as a source address of the reply. Select the default
+ // address instead!
+ if (!iInterfacer->LocalScope(TInetAddr::Cast(info->iSrcAddr).Ip6Address(), aInfo->iInterfaceIndex, EScopeType_IF))
+ TInetAddr::Cast(info->iSrcAddr).SetAddress(KInet6AddrNone);
+ if (info->iProtocol == STATIC_CAST(TInt, KProtocolInetIcmp))
+ echo.iHdr->SetType((TUint8)0);
+ else
+ echo.iHdr->SetType(KInet6ICMP_EchoReply);
+ if (info->iFlow.Open(iNetwork, info->iProtocol) == KErrNone)
+ {
+ info->iFlow.SetRemoteAddr(info->iDstAddr);
+ info->iFlow.SetLocalAddr(info->iSrcAddr);
+ info->iFlow.SetIcmpType(echo.iHdr->Type());
+ info->iFlow.FlowContext()->SetOption(KSolInetIp, KSoKeepInterfaceUp, KInetOptionDisable);
+ info->iFlow.Connect();
+#ifdef SYMBIAN_TCPIPDHCP_UPDATE
+ // Need to Handle NUT replaces the oldest ND entry with the new arrival packet, when queue overflows.
+ if ( (info->iFlow.Status() == KErrNone ) ||
+ ( (info->iFlow.Status() == EFlow_HOLD) && (info->iFlow.IsNdResolutionPending()) )//[RFC-4861]
+ )
+#else
+ if (info->iFlow.Status() == KErrNone)
+#endif // SYMBIAN_TCPIPDHCP_UPDATE
+
+ {
+ // Successfully connected
+ // Connection might have changed or redefined the src address. Must update
+ // the info block with it.
+ info->iSrcAddr = info->iFlow.FlowContext()->LocalAddr();
+ info->iFlags = 0;
+ // IPv4 ICMP checksum does not use "pseudoheader" => pass NULL for info when IPv4!
+ echo.ComputeChecksum(packet, info->iProtocol == STATIC_CAST(TInt, KProtocolInetIcmp) ? NULL : info);
+ packet.Pack();
+ Send(packet);
+ return;
+ }
+ info->iFlow.Close();
+ }
+ }
+ packet.Free();
+ }
+
+//
+// CProtocolIP::IcmpHandlerL
+// *************************
+// A shared ICMP handler for IPv4 and IPv6. Even if the code clearly splits
+// into two totally different parts, it allows easy handling of ICMP6 within
+// IPv4 or ICMP4 within IPv6 (whether such bogosities are legal or not).
+//
+// The caller must trap the leave and release the packet!
+//
+TInt CProtocolIP::IcmpHandlerL(RMBufHookPacket &aPacket, RMBufRecvInfo &aInfo, CProtocolBase *aFinalTarget)
+ {
+ CHookEntry *h;
+ CProtocolBase *target = NULL;
+
+ TInet6Checksum<TInet6HeaderICMP> icmp(aPacket, aInfo.iOffset);
+ if (icmp.iHdr == NULL)
+ goto drop_packet;
+ //
+ // This assert should really be somehow in the inet.h, but for the time
+ // being it is here... (e.g. Info blocks *MUST* fit into single RMBuf block!)
+ // -- msa
+ ASSERT(sizeof(RMBufRecvInfo) <= STATIC_CAST(TUint, KMBufSmallSize));
+
+
+ if (!icmp.VerifyChecksum(aPacket, aInfo.iProtocol == STATIC_CAST(TInt, KProtocolInet6Icmp) ? &aInfo : NULL, aInfo.iOffset))
+ goto drop_packet;
+ //
+ // Fill in the ICMP portion of the RMBufIcmpInfo
+ // (Note, that iIcmp is still ZERO, just preloading the information for easy access)
+ //
+ aInfo.iType = icmp.iHdr->Type();
+ aInfo.iCode = icmp.iHdr->Code();
+ aInfo.iParameter = icmp.iHdr->Parameter();
+
+ switch (ClassifyIcmp(aInfo.iProtocol, aInfo.iType))
+ {
+ case EIcmpType_ECHO:
+ IcmpEcho(aPacket, &aInfo);
+ return -1;
+ case EIcmpType_ERROR:
+ break; // Fall to Error processing
+ case EIcmpType_OTHER:
+ return iInterfacer->IcmpHandler(aPacket, aInfo);
+ default:
+ goto drop_packet; // ...weird...
+ }
+
+#if 1
+ // The error processing is going to "eat" the ICMP error packet. If we have
+ // some application reading ICMP socket, it would not see these packets. To
+ // enable this feature, a separate copy must be explicitly made and passed.
+ //
+ if (aFinalTarget && aFinalTarget != iNullHook->iProtocol)
+ {
+ RMBufPacketBase copy;
+ TRAPD(err, aPacket.CopyL(copy); aPacket.CopyInfoL(copy););
+ if (err == KErrNone)
+ {
+ copy.Pack();
+ aFinalTarget->Process(copy, this);
+ }
+ copy.Free();
+ }
+#endif
+
+
+ //
+ // ICMP Error dispatching loop
+ // ---------------------------
+ // (first access the problem packet and setup the info block accordingly)
+ //
+ aInfo.iIcmp = (TUint8)aInfo.iProtocol; // Start ICMP error buffer processing
+ aInfo.iOffset += sizeof(TInet6HeaderICMP); // ..points first octet after ICMP header (v4 or v6)
+ aInfo.iOffsetIp = (TUint16)aInfo.iOffset;
+ if (aInfo.iOffset > aInfo.iLength)
+ goto drop_packet;
+
+ {
+ // Get interface (needed for scopes)
+ const MInterface *const mi = iInterfacer->Interface(aInfo.iInterfaceIndex);
+ if (mi == NULL)
+ goto drop_packet;
+
+ TInet6Packet<TIpHeader> ip(aPacket, aInfo.iOffsetIp);
+ if (ip.iHdr == NULL)
+ goto drop_packet;
+ aInfo.iVersion = (TUint8)ip.iHdr->ip4.Version();
+
+ TInetAddr &src = TInetAddr::Cast(aInfo.iSrcAddr);
+ TInetAddr &dst = TInetAddr::Cast(aInfo.iDstAddr);
+ switch (aInfo.iVersion)
+ {
+ case 6:
+ //
+ // Set Info addresses from the IPv6 header of the problem packet
+ //
+ if (ip.iLength < (TInt)sizeof(TInet6HeaderIP))
+ goto drop_packet; // Not useful, trunctated IP header.
+ src.SetAddress(ip.iHdr->ip6.SrcAddr());
+ src.SetFlowLabel(ip.iHdr->ip6.FlowLabel());
+ dst.SetAddress(ip.iHdr->ip6.DstAddr());
+ aInfo.iPrevNextHdr = (TUint16)(aInfo.iOffset + TInet6HeaderIP::O_NextHeader);
+ aInfo.iOffset += sizeof(TInet6HeaderIP);
+ aInfo.iProtocol = ip.iHdr->ip6.NextHeader(); // Always in range [0 .. 255] (TUint8)
+ break;
+ case 4:
+ {
+ // Need to do sanity check on IPv4 header length!
+ const TInt hlen = ip.iHdr->ip4.HeaderLength();
+ if (ip.iLength < hlen || hlen < TInet6HeaderIP4::MinHeaderLength())
+ goto drop_packet;
+ //
+ // Set Info addresses from the IPv4 header of the problem packet
+ //
+ src.SetV4MappedAddress(ip.iHdr->ip4.SrcAddr());
+ dst.SetV4MappedAddress(ip.iHdr->ip4.DstAddr());
+ aInfo.iPrevNextHdr = (TUint16)(aInfo.iOffset + TInet6HeaderIP4::O_Protocol);
+ aInfo.iOffset += hlen;
+ aInfo.iProtocol = ip.iHdr->ip4.Protocol(); // Always in range [0 .. 255] (TUint8)
+ }
+ break;
+ default:
+ goto drop_packet;
+ }
+ // Complete scope id values
+ src.SetScope(mi->Scope((TScopeType)(src.Ip6Address().Scope()-1)));
+ dst.SetScope(mi->Scope((TScopeType)(dst.Ip6Address().Scope()-1)));
+ }
+ //
+ // Let the interface manager have a peek at the packet
+ //
+ if (iInterfacer->IcmpError(aPacket, aInfo) < 0)
+ return -1; // Packet was owned/dropped in iInterfacer method!
+
+again:
+ for (h = iSwitch[aInfo.iProtocol].iHead;;)
+ {
+ if (h->IsProtocol())
+ {
+ if (target)
+ {
+ if (target == this)
+ {
+ // A temporary solution for "self-detunnel", which
+ // is not prepared to get "iOffset" in the info
+ // structure (cannot trust it, because normal Process
+ // calls come from the drivers). Need propably do a
+ // separate detunneling pseudoprotocol, even though
+ // linking self is a "cute trick".. -- msa
+ break; // CANNOT HANDLE THIS IN ANY WAY, JUST DROP!!
+ }
+ if (aInfo.iLength > aInfo.iOffset) // Trust iLength properly maintained!!
+ {
+ aPacket.Pack();
+ target->Process(aPacket);
+ return -1;
+ }
+ else
+ break; // All of the packet processed, just drop.
+ }
+ //
+ // Target protocol found (Phase 1 done)
+ //
+ target = h->iProtocol; // Always != NULL!!
+ //
+ // Now, process all generic hook(s), if any registered
+ //
+ h = iSwitch[KIp6Hook_ANY].iHead;
+ }
+ else
+ {
+ //
+ // *NOTE*
+ // The error ApplyL method is not supposed to touch the info (iLength),
+ // It only must update the head iOffset field!!! -- msa
+ //
+ const TInt ret = h->iHook->ApplyL(aPacket, aInfo);
+ if (ret < 0)
+ {
+ ASSERT(aPacket.IsEmpty());
+ return -1; // The hook consumed the packet.
+ }
+ else
+ {
+ LOG(VerifyPacket(aPacket, aInfo));// Hook Sanity checks!
+ if (ret == KIp6Hook_DONE)
+ {
+ if (aInfo.iProtocol != 0)
+ goto again; // The hook changed Next header (restart)
+ else
+ goto drop_packet; // Hop-by-hop is only valid after IP header!
+ }
+ }
+ h = h->iNext;
+ }
+ }
+ // Plain exit from the above loop is "drop packet" (consume)
+drop_packet:
+ LOG(Log::Printf(_L("\t%S IcmpHandler: packet dropped"), &ProtocolName()));
+ aPacket.Free();
+ return -1;
+ }
+
+void CProtocolIP::IcmpSend(TInt aProtocol, RMBufRecvPacket &aPacket, TInt aType, TInt aCode, TUint32 aParameter, TInt aMC)
+ {
+ RMBufRecvInfo *info = aPacket.Info();
+ RMBufSendPacket packet;
+ RMBufSendInfo *snd = NULL;
+
+ for (;;)
+ {
+ if (info == NULL || info->iIcmp || (info->iFlags & KIpNeverIcmpError) != 0)
+ break;
+ if (iIcmpThrottle.Suppress())
+ break; // Drop! Too many sent
+
+ const TIpHeader *const ip = ((RMBufPacketPeek &)aPacket).GetIpHeader();
+ if (!ip)
+ break;
+
+ TInetAddr src, dst;
+ switch (ip->ip4.Version())
+ {
+ case 4:
+ src.SetV4MappedAddress(ip->ip4.DstAddr());
+ dst.SetV4MappedAddress(ip->ip4.SrcAddr());
+ break;
+ case 6:
+ src.SetAddress(ip->ip6.DstAddr());
+ dst.SetAddress(ip->ip6.SrcAddr());
+ break;
+ default:
+ goto cleanup;
+ }
+
+ dst.SetScope(iInterfacer->RemoteScope(dst.Ip6Address(), info->iInterfaceIndex, EScopeType_IF));
+ // Verify and adjust addresses
+ // If the packet was addressed directly to me, use the same address
+ // as a source in the complaint.
+ const TUint32 src_id = iInterfacer->LocalScope(src.Ip6Address(), info->iInterfaceIndex, EScopeType_IF);
+ if (src_id)
+ {
+ // The packet was addressed directly to me, use the same address
+ // as a source in the complaint.
+ //
+ // However, the src address scope >= dst address scope
+ //
+ if (dst.Ip6Address().Scope() < src.Ip6Address().Scope())
+ break; // Do not send ICMP Error
+ if ((info->iFlags & KIpBroadcastOnLink) != 0 && !aMC)
+ break; // Was broadcast on link, and replies to multicast not allowed.
+ src.SetScope(src_id);
+ }
+ else if (aMC || (src.IsUnicast() &&
+ (info->iFlags & KIpBroadcastOnLink) == 0 &&
+ !iInterfacer->IsForMeAddress(src.Ip6Address(), info->iInterfaceIndex)))
+ {
+ // This must be some type of forwarding or multicast destination
+ // instead, let the flow open decide on it. Note, the caller of
+ // this method is responsible for allowing (aMC != 0) generating
+ // ICMP's for multicast destinations! (In some cases sending
+ // replies to multicast is required by RFC's, can't prevent it
+ // at this level! -- msa)
+ src.SetAddress(KInet6AddrNone);
+ src.SetScope(info->iInterfaceIndex);
+ }
+ else
+ break;
+
+ TRAPD(err, snd = packet.CreateL(8));
+ if (err != KErrNone || snd == NULL)
+ break;
+
+ // Create unconnected flow context to the packet (info)
+ if (snd->iFlow.Open(this, aProtocol) != KErrNone)
+ break;
+ // Setup the connection information and connect
+ snd->iFlow.SetRemoteAddr(dst);
+ snd->iFlow.SetLocalAddr(src);
+ snd->iFlow.SetIcmpType(aType, aCode);
+ //
+ // Supply the original packet as a flow parameter
+ // (transfer the aPacket as is into the flow context)
+ CFlowContext *const flow = snd->iFlow.FlowContext();
+ aPacket.SetInfo(NULL);
+ flow->iHead.iIcmp.Assign(aPacket);
+ flow->iHead.iIcmp.SetInfo(info);
+ // Disable "keep interface up" for ICMP error replies
+ flow->SetOption(KSolInetIp, KSoKeepInterfaceUp, KInetOptionDisable);
+ //
+ snd->iFlow.Connect();
+ if (snd->iFlow.Status() != KErrNone)
+ break; // Cannot get a flow opened..
+ if (flow->iHead.iIcmp.IsEmpty() ||
+ (info = (RMBufRecvInfo *)flow->iHead.iIcmp.Info()) == NULL)
+ break; // Oops, apparently some hook ate the packet/info
+ ASSERT(info->iLength == flow->iHead.iIcmp.Length());
+
+ const TIpHeader *const ip2 = ((RMBufPacketPeek &)flow->iHead.iIcmp).GetIpHeader();
+ if (!ip2)
+ break;
+ //
+ // Trim the returned packet for the ICMP error payload
+ //
+ switch (ip2->ip4.Version())
+ {
+ case 4:
+ // For IPv4, ICMP error payload includes the original IP header
+ // and at most 8 bytes following it.
+ if (info->iLength > ip2->ip4.HeaderLength() + 8)
+ flow->iHead.iIcmp.TrimEnd(ip2->ip4.HeaderLength() + 8);
+ // TrimEnd updates info->iLength!
+ break;
+ case 6:
+ {
+ // For IPv6, ICMP error payload inludes as much of the original
+ // packet as can be fit into minimum MTU for IPv6
+ const TInt available = KInet6MinMtu - flow->HeaderSize() - snd->iLength;
+ if (available < 0)
+ goto cleanup; // flow headers take too much space! (bad hooks?)
+ else if (available < info->iLength)
+ flow->iHead.iIcmp.TrimEnd(available);
+ // TrimEnd updates info->iLength!!
+ }
+ break;
+ default:
+ goto cleanup;
+ }
+ // Append original packet to the error report
+ packet.Append(flow->iHead.iIcmp);
+ snd->iLength += info->iLength;
+ flow->iHead.iIcmp.Free();
+
+ snd->iSrcAddr = flow->LocalAddr();
+ snd->iDstAddr = flow->RemoteAddr();
+ snd->iProtocol = aProtocol;
+
+ TInet6Checksum<TInet6HeaderICMP> icmp(packet);
+ if (icmp.iHdr == NULL)
+ break;
+
+ icmp.iHdr->SetType((TUint8)aType);
+ icmp.iHdr->SetCode((TUint8)aCode);
+ icmp.iHdr->SetParameter(aParameter);
+ icmp.ComputeChecksum(packet, aProtocol == STATIC_CAST(TUint, KProtocolInetIcmp) ? NULL : snd);
+ packet.Pack();
+ Send(packet);
+ return;
+ }
+cleanup:
+ // Release resources that didn't get passed on
+ aPacket.Free();
+ if (snd != NULL)
+ snd->iFlow.Close();
+ packet.Free();
+ }
+
+//
+// CProtocolIP::Icmp4Send
+// **********************
+// Build IPv4 ICMP message from the received packet.
+//
+// The packet is assumed to be in *UNPACKED STATE* (Info block separated!)
+//
+void CProtocolIP::Icmp4Send(RMBufRecvPacket &aPacket, TInt aType, TInt aCode, TUint32 aParameter, TInt aMC)
+ {
+ IcmpSend(KProtocolInetIcmp, aPacket, aType, aCode, aParameter, aMC);
+ }
+//
+// CProtocolIP::Icmp6Send
+// **********************
+// Build IPv6 ICMP message from the received packet.
+//
+// The packet is assumed to be in *UNPACKED STATE* (Info block separated!)
+//
+void CProtocolIP::Icmp6Send(RMBufRecvPacket &aPacket, TInt aType, TInt aCode, TUint32 aParameter, TInt aMC)
+ {
+ IcmpSend(KProtocolInet6Icmp, aPacket, aType, aCode, aParameter, aMC);
+ }
+
+// CProtocolIP::IcmpWrap
+// *********************
+/**
+// Wrap a packet into ICMP error reply and send it out.
+//
+// The aPacket is assumed to hold a complete IP packet (starting from the beginning)
+// in a packed state. The info block is initialized by this method (the current
+// content is ignored).
+//
+// Determine the IP version from the IP header in the packet and choose the ICMP
+// type and code accordingly from aIcmp, and then call the actual ICMP sending
+// code (either Icmp4Send or Icmp6Send).
+//
+// On any error or problems, the packet is simply released, and no ICMP message
+// will occur.
+//
+// @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 CProtocolIP::IcmpWrap(RMBufChain &aPacket, const TIcmpTypeCode aIcmp, const TUint32 aParameter, const TInt aMC)
+ {
+ if (aPacket.IsEmpty())
+ return; // nothing to do with empty packet!
+
+ // Use "info" as receive info, because this is what the
+ // actual ICMP sending code (Icmp4Send/Icmp6Send) expects.
+ RMBufRecvPacket packet;
+ packet.Assign(aPacket);
+ packet.Unpack();
+ RMBufRecvInfo *const info = packet.Info();
+
+ if (info)
+ {
+ // Setup "fake" RMBufRecvInfo (just enough to pass
+ // Icmp4Send/Icmp6Send inspections...)
+ info->iIcmp = 0; // Prevent it being from dropped
+ info->iOffset = 0; // not used?
+ info->iFlags = 0;
+ //
+ // *note*
+ // Icmp4Send/Icmp6Send are designed to return incoming packet
+ // and they use iInterfaceIndex to return the error message
+ // to the original interface. However, this method is mostly
+ // used for outgoing packets from this host (source address
+ // is usually my own address), there is no "originating
+ // interface". Use ZERO, and assume ICMP sending methods
+ // handle this correctly.
+ //
+ // When forwarding is enabled, the packet could actually have
+ // come from an other interface. Unfortunately, in current
+ // version, the informatio about originating interface has
+ // beeen lost at this point. It would be more correct to
+ // force the ICMP error back to original interface instead
+ // of relying on source address and routing to choose it.
+ // [needs to be examined later -- msa]
+ info->iInterfaceIndex = 0;
+
+ const TIpHeader *const ip = ((RMBufPacketPeek &)packet).GetIpHeader();
+ if (ip)
+ {
+ if (ip->ip4.Version() == 4)
+ Icmp4Send(packet, aIcmp.i4type, aIcmp.i4code, aParameter, aMC);
+ else if (ip->ip6.Version() == 6)
+ Icmp6Send(packet, aIcmp.i6type, aIcmp.i6code, aParameter, aMC);
+ }
+ }
+ //
+ // Release packet (if not passed on)
+ //
+ packet.Free();
+ return;
+ }
+
+
+//
+// **********************
+// Flow Network Interface
+// **********************
+//
+// CProtocolIP::NewFlowL
+// **********************
+//
+CFlowContext *CProtocolIP::NewFlowL(const void *aOwner, TUint aProtocol)
+ {
+ if (iNetwork != this && iNetwork) // There are two instances, etc...
+ return iNetwork->NewFlowL(aOwner, aProtocol);
+ return iInterfacer->NewFlowL(aOwner, this, aProtocol); // <-- either leaves or succeeds!
+ }
+
+CFlowContext *CProtocolIP::NewFlowL(const void *aOwner, CFlowContext &aFlow)
+ {
+ //
+ // Assume the 'aFlow' provides all the necessary non-zero defaults
+ // and no other inits are required.
+ //
+ if (iNetwork != this && iNetwork) // There are two instances, etc...
+ return iNetwork->NewFlowL(aOwner, aFlow);
+ return iInterfacer->NewFlowL(aOwner, this, aFlow); // <-- either leaves or succeeds!
+ }
+
+// CProtocolIP::SetChanged
+// ***********************
+//
+TInt CProtocolIP::SetChanged() const
+ {
+ return iInterfacer->SetChanged();
+ }
+//
+// CProtocolIP::FlowSetupHooks
+// **************************
+// (common for both IPv6 and IPv4)
+//
+TInt CProtocolIP::FlowSetupHooks(CFlowInternalContext &aFlow)
+ {
+ if (iNetwork != this && iNetwork) // There are two instances, etc...
+ return iNetwork->FlowSetupHooks(aFlow);
+
+ //
+ // Attach registered hooks
+ // For each registered outbound hook, ask if it wants to attach to
+ // the current flow or not. If it wants, add a flow hook entry.
+ //
+ for (CHookEntry *h = iOutbound.iHead; h != NULL; h = h->iNext)
+ {
+ MFlowHook *hook = NULL;
+
+ const TIp6Addr src = aFlow.Head().ip6.SrcAddr();
+ const TUint32 src_id = aFlow.Head().iSrcId;
+ const TIp6Addr dst = aFlow.Head().ip6.DstAddr();
+ const TUint32 dst_id = aFlow.Head().iDstId;
+ const TUint set = aFlow.Head().iSourceSet;
+ TInt frag = aFlow.Head().iFragment ? -1 : aFlow.HeaderSize();
+#ifdef _LOG
+ TServerProtocolDesc info;
+ h->iHook->Identify(&info);
+#endif
+ TRAPD(ret, hook = h->iHook->OpenL(aFlow.Head(), &aFlow););
+ if (ret == KErrNone)
+ {
+ if (frag < 0)
+ {
+ // Fragging has already been requested before, just make
+ // sure the iFragment stays set (hook cannot clear it!)
+ ASSERT(aFlow.Head().iFragment == 1);
+ aFlow.Head().iFragment = 1;
+ }
+ else if (aFlow.Head().iFragment == 0)
+ // This hook is not requesting fragmentation.
+ frag = -1;
+
+ // frag is non-negative only if this hook requested the
+ // fragmentaion, and is the first one to do so.
+ if (hook)
+ {
+
+ LOG(Log::Printf(_L("\t\tFlow[%u] %S attaching MFlowHook[%u] frag=%d"), &aFlow, &info.iName, hook, frag));
+ TRAP(ret, aFlow.AddHookL(hook, frag));
+ hook->Close(); // Cancel my OpenL reference
+ if (ret == KErrNone)
+ {
+ if (src.IsEqual(aFlow.Head().ip6.SrcAddr()) &&
+ src_id == aFlow.Head().iSrcId &&
+ dst.IsEqual(aFlow.Head().ip6.DstAddr()) &&
+ dst_id == aFlow.Head().iDstId &&
+ set == aFlow.Head().iSourceSet)
+ continue; // Unchanged routing info
+ if ((ret = aFlow.RouteFlow(aFlow.Head())) == KErrNone)
+ continue;
+ }
+ }
+ else if (frag < 0)
+ continue; // Not interested!
+ else
+ {
+ // Hook requested fragmentation, but didn't attach to the flow. This
+ // is not allowed!
+ ret = KErrGeneral;
+ }
+ }
+ //
+ // Error condition, Abort Open/Connect sequence
+ //
+ aFlow.RemoveHooks();
+ LOG(Log::Printf(_L("\t\tFLow[%u] OpenL aborted by %S with reason=%d"), &aFlow, &info.iName, ret));
+ return ret;
+ }
+
+ return KErrNone;
+ }
+
+
+void CProtocolIP::FlowStartRefresh(CFlowInternalContext &aFlow)
+ {
+ if (aFlow.iHead.ip6.Version() == 4)
+ aFlow.iHdrSize += TInet6HeaderIP4::MinHeaderLength();
+ else
+ aFlow.iHdrSize += TInet6HeaderIP::MinHeaderLength();
+ }
+
+TInt CProtocolIP::GetFlowOption(TUint aLevel, TUint aName, TDes8 &aOption, const CFlowContext &aFlow) const
+ {
+ if ((const MNetworkService*)iNetwork != this && iNetwork) // There are two instances, etc...
+ return iNetwork->GetFlowOption(aLevel, aName, aOption, aFlow);
+
+ //
+ // Go through the output hooks and try to Get the options
+ //
+ TInt ret = KErrNotSupported;
+ for (CHookEntry *h = iOutbound.iHead; h != NULL; h = h->iNext) {
+ if ((ret = h->iHook->GetFlowOption(aLevel, aName, aOption, aFlow)) != KErrNotSupported)
+ break;
+ }
+ return ret;
+ }
+
+TInt CProtocolIP::SetFlowOption(TUint aLevel, TUint aName, const TDesC8 &aOption, CFlowContext &aFlow)
+ {
+ if (iNetwork != this && iNetwork) // There are two instances, etc...
+ return iNetwork->SetFlowOption(aLevel, aName, aOption, aFlow);
+ //
+ // Go through the output hooks and try to Set the options
+ //
+ TInt ret = KErrNotSupported;
+ for (CHookEntry *h = iOutbound.iHead; h != NULL; h = h->iNext) {
+ if ((ret = h->iHook->SetFlowOption(aLevel, aName, aOption, aFlow)) != KErrNotSupported)
+ break;
+ }
+ return ret;
+ }