diff -r 000000000000 -r af10295192d8 networkprotocols/tcpipv4v6prt/src/ip6.cpp --- /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 +#include +#include +#include +#include +#include +#include // only for KIpBroadcastOnLink +#include "ip6.h" +#include +#include +#include "ip6_rth.h" +#include "ip6_doh.h" +#include "ip6_frag.h" +#include "in_flow.h" +#include "iface.h" +#include "in_net.h" +#include +#include +#include "loop6.h" +#include "res.h" // only for KProtocolInet6Res +#include "addr46.h" +#include "tcpip_ini.h" +#include + +// 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 +#endif + +static const TLitC8 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 *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(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 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 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 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 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 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 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 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 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 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 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 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; + }