diff -r 000000000000 -r af10295192d8 networkprotocols/tcpipv4v6prt/src/in_flow.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networkprotocols/tcpipv4v6prt/src/in_flow.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,277 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// in_flow.cpp - stack side of the general part of the flow +// + +#include +#include "in_flow.h" +#include "inet6log.h" +#include "iface.h" + +static const TUint16 KNoFrag = 0xFFFF; +// +// TFlowHook +// +class TFlowHook + { +public: + TFlowHook(MFlowHook &aHook, TInt aFrag) : iHook(aHook), iFrag(aFrag < 0 ? KNoFrag : (TUint16)aFrag) {} + MFlowHook &iHook; //< The Flow Hook + const TUint16 iFrag; //< The header size before OpenL (or 0xFFFF, if no fragmenting) + TUint16 iMtu; //< The Fragmenting MTU (only if iFrag != 0xFFFF) + }; + +CFlowInternalContext::~CFlowInternalContext() + { + RemoveHooks(); + } + +// ********************** +// CFlowContext::AddHookL +// ********************** +// aHook must never be NULL! +// +void CFlowInternalContext::AddHookL(MFlowHook *aHook, TInt aFrag) + { + // + // Allow call with NULL aHook, does nothing then... + // + if (!aHook) + return; + // + // iHookList is created only if actual hooks are required + // + if (iHookList == NULL) + iHookList = new (ELeave) CArrayFixFlat(4); + TFlowHook h(*aHook, aFrag); + iHookList->AppendL(h); + aHook->Open(); + } + +// ************************* +// CFlowContext::RemoveHooks +// ************************* +// +void CFlowInternalContext::RemoveHooks() + { + // + // Remove hooks + // + if (iHookList) + { + for (TInt i = iHookList->Count(); i > 0; ) + { + const TFlowHook &h = iHookList->At(--i); + h.iHook.Close(); + } + delete iHookList; + iHookList = NULL; + } + } + +// ******************* +// CFlowContext::Start +// ******************* +// Must be called once when the flow Open/Hook adding has +// been completed, but before calling the hook Ready methods +// +void CFlowInternalContext::Start() + { + __ASSERT_DEBUG(iHead.iPacket.IsEmpty(), User::Panic(_L("iHead.iPacket not empty"), 0)); + + iStart = iHead; // Just save a copy of the iHead. + iStart.iOffset = iHdrSize; // Borrow the iOffset field to hold the initial header overhead + } + +// ******************* +// CFlowContext::Reset +// ******************* +// Can be called any time to set the flow into initial state +// (called before hook Ready method calls) +// +void CFlowInternalContext::Reset() + { + iHead.iPacket.Free(); + iHead = iStart; + // + // Reset the initial (after Open-phase) header overhead + // + iHdrSize = iHead.iOffset; + iHead.iOffset = 0; + } + +// +// CFlowContext::RefreshHooks +// ************************** +// (Utility for derived classes) +// Call the Ready() methods of the registered hooks +// and check if all of them are ready. If so, the state +// of the flow will be EFlow_READY, otherwise the state +// will be the state of the first hook that reports +// not READY. +// +void CFlowInternalContext::RefreshHooks() + { + TInt err; + iStatus = EFlow_READY; + if (iHookList) + { + LOG(const MFlowHook *tmp = NULL); + TRAP(err, + for (TInt i = iHookList->Count(); iStatus == EFlow_READY && i > 0; ) + { + TFlowHook &h = iHookList->At(--i); + iStatus = h.iHook.ReadyL(iHead); + LOG(tmp = &h.iHook); + // Compute fragmenting MTU, if fragmenting is requested + // (result is garbage if iFrag == KNoFrag, but it does not matter) + h.iMtu = (TUint16)(PathMtu() - HeaderSize() - h.iFrag); + } + ); // TRAP + if (err != KErrNone) + { + LOG(Log::Printf(_L("\tMFlowHook[%u] ReadyL LEAVE WITH err=%d"), (TInt)tmp, err)); + // *NOTE* + // Must ensure that the status will be negative, + // because positive values are interpreted as + // "pending" signals and a later wakeup is + // expected. It is doubtful that a hook leaves + // unexpectedly and still has set for the wakeup! + // ... though, I suppose this should never + // happen? Leave errors are always negative? + // -- msa + // Force KErrGeneral, if err is not negative! + iStatus = err < 0 ? err : KErrGeneral; + } + LOG(if (iStatus != EFlow_READY) Log::Printf(_L("\tMFlowHook[%u] ReadyL iStatus = %d"), (TInt)tmp, iStatus)); + } + } + +// CFlowInternalContext::ApplyHooksL +// ********************************** +TInt CFlowInternalContext::ApplyHooksL(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo) + { + const TInt n = iHookList->Count(); + for (TInt i = 0; i < n; ) + { + const TFlowHook &h = iHookList->At(i++); + if (h.iFrag != KNoFrag && aInfo.iLength > h.iMtu) + return --i; + + const TInt ret = h.iHook.ApplyL(aPacket, aInfo); + if (ret > 0) + { + LOG(Log::Printf(_L("\tMFlowHook[%u] ApplyL returns %d, restarat ApplyL (*deprecated feature used*)"), (TInt)&h.iHook, ret)); + i = 0; // Restart hooks. + } + else if (ret < 0) + { + LOG(Log::Printf(_L("\tMFlowHook[%u] ApplyL returns %d, packet thrown away"), (TInt)&h.iHook, ret)); + User::Leave(ret); + } + else if (iHookList == NULL) + { + // Returned KErrNone, but the iHookList has disappeared + LOG(Log::Printf(_L("\tMFlowHook[%u] ApplyL disconnected the flow"), (TInt)&h.iHook)); + User::Leave(KErrDisconnected); + } + } + return n; + } + +void CFlowInternalContext::ApplyHooksFragmentedL(TInt aStart, RMBufSendPacket &aPacket) + { + RMBufSendInfo *info = aPacket.Unpack(); + const TInt n = iHookList->Count(); + for (TInt i = aStart; i < n; ) + { + const TFlowHook &h = iHookList->At(i++); + const TInt ret = h.iHook.ApplyL(aPacket, *info); + if (ret > 0) + { + LOG(Log::Printf(_L("\tMFlowHook[%u] ApplyL (fragment) returns %d, restart ApplyL (*deprecated feature used*)"), (TInt)&h.iHook, ret)); + i = 0; // Restart hooks. + } + else if (ret < 0) + { + LOG(Log::Printf(_L("\tMFlowHook[%u] ApplyL (fragment) returns %d"), (TInt)&h.iHook, ret)); + User::Leave(ret); + } + else if (iHookList == NULL) + { + // Returned KErrNone, but the iHookList has disappeared + LOG(Log::Printf(_L("\tMFlowHook[%u] ApplyL (fragment) disconnected the flow"), (TInt)&h.iHook)); + User::Leave(KErrDisconnected); + } + } + aPacket.Pack(); + } +// +// CFlowContext::ApplyHooks +// ************************ +// Apply hooks on flow to the packet. This has only two possible +// returns +// +// == KErrNone - hooks applied successfully +// != KErrNone - error occurred, packet has been released +// +TInt CFlowInternalContext::ApplyHooks(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo, RMBufPktQ &aFragments, MNetworkServiceExtension &aExt) + { + TInt ret = KErrNone; + if (iHookList) + { + TInt n = 0; + TRAPD(err, n = ApplyHooksL(aPacket, aInfo)); + if (err != KErrNone) + { + // The ApplyL has left, free packet! + LOG(Log::Printf(_L("\tApplyHooks (ApplyL) dropping packet with reason %d"), err)); + if (!aPacket.IsEmpty()) + { + LOG(Log::Printf(_L("\tApplyHooks (ApplyL) packet is not empty, closing flow reference"))); + aInfo.iFlow.Close(); + } + aPacket.Free(); + return err; + } + if (n < iHookList->Count()) + { + // Packet too long for MTU and hook wants fragmenting + // done before it. + RMBufPktQ fragments; + if (aExt.Fragment(aPacket, aInfo, iHookList->At(n).iMtu, fragments)) + { + TRAP(err, + while (fragments.Remove(aPacket)) + { + ApplyHooksFragmentedL(n, aPacket); + aFragments.Append(aPacket); + } + ); + ret = err; + } + aFragments.Append(fragments); // Just in case some was left there... + LOG(if (ret != KErrNone) Log::Printf(_L("\tApplyHooks (ApplyL fragments) returning %d"), err)); + return ret; + } + } + if (aInfo.iLength > PathMtu() && !aExt.Fragment(aPacket, aInfo, PathMtu(), aFragments)) + { + // Fragmentation required, but failed for some reason. The loopback ICMP has already been sent, + // Just return something != KErrNone for the caller to indicate that the aPacket has been + // processed (and should be empty at this point). + ret = KErrNotSupported; + } + return ret; + }