networkprotocols/tcpipv4v6prt/src/in_flow.cpp
changeset 0 af10295192d8
--- /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 <es_mbuf.h>
+#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<TFlowHook>(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;
+	}