networkcontrol/qosfwconfig/qos/src/flowhook.cpp
changeset 0 af10295192d8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkcontrol/qosfwconfig/qos/src/flowhook.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,718 @@
+// 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:
+//
+
+#include <udp_hdr.h>
+#include <tcp_hdr.h>
+#include <icmp6_hdr.h>
+#include <ip4_hdr.h>
+#include <ip6_hdr.h>
+
+#include "qos_prot.h"
+#include "flowhook.h"
+#include "negotiation.h"
+#include "qoserr.h"
+#include "interface.h"
+#include "modules.h"
+#include "policy_mgr.h"
+#include "qos_channel.h"
+#include <es_prot_internal.h>
+
+CFlowHook::CFlowHook(CProtocolQoS& aProtocol, CFlowContext& aContext) : iProtocol(aProtocol), iContext(aContext)
+	{
+	LOG(Log::Printf(_L("new\tqos HOOK[%u] on FLOW[%u] size=%d"), (TInt)this, (TInt)&aContext, sizeof(CFlowHook)));
+	iProtocol.Open();	// The CProtocolQoS cannot be deleted until all flows have been closed!
+	iInterface = NULL;
+	iRefCount = 0;
+	iSession = NULL;
+	iFlowStatus = EFlow_READY;
+	iChannel = NULL;
+	TSoOwnerInfo owner_info;
+	TPckg<TSoOwnerInfo> option(owner_info);
+	TInt ret = iContext.RetrieveOption(KSOLProvider, KSoOwnerInfo, option);
+	if (ret == KErrNone)
+		{
+		iUid = owner_info.iUid;
+		LOG(Log::Printf(_L("\tqos HOOK[%u] Ownerinfo UID [0x%x 0x%x 0x%x]"),
+			(TInt)this,
+			owner_info.iUid[0].iUid,
+			owner_info.iUid[1].iUid,
+			owner_info.iUid[2].iUid ));
+		}
+	// Turn off HeaderMode flag
+	// (so that we don't think QoS has changed in ReadyL, if nothing has been specified)
+	iQoS.SetHeaderMode(EFalse);
+	}
+
+CFlowHook::~CFlowHook()
+	{
+	iProtocol.Close();	// May trigger final destruction of CProtocolQoS!
+	LOG(Log::Printf(_L("~\tqos HOOK[%u] on FLOW[%u] destructor completed"), (TInt)this, (TInt)&iContext));
+	}
+
+
+void CFlowHook::Open()
+	{
+	iRefCount++;
+	}
+
+void CFlowHook::Close()
+	{
+	if (--iRefCount < 0)
+		{
+		LOG(Log::Printf(_L("Close\tqos HOOK[%u] on FLOW[%u] closed with refs=0 -- start shutdown"), (TInt)this, (TInt)&iContext));
+		CloseQoS();
+		iHookLink.Deque();
+		delete this;
+		}
+	}
+
+
+void CFlowHook::FillFlowInfo(TPfqosMessage& aMsg, pfqos_address& aSrc, pfqos_address& aDst, pfqos_selector& aSel, TInetAddr& aMsk)
+	/**
+	* Fill in flow specific information for PF_QOS Message.
+	*
+	* Initialize three extesion (EPfqosExtSrcAddress, EPfqosExtDstAddress and EPfqosExtSelector) into
+	* TPfqosMessage. The caller must provide the space for the required extension blocks. This only
+	* initializes the blocks and sets the iExt references to the aMsg.
+ 	*
+	*
+	* @param aMsg The PF_QOS Message to be used
+	* @param aSrc The source address block for the Msg
+	* @param aDst The destination address block for the Msg
+	* @param aSel The selector block for the Msg.
+	* @param aMsk Intialized to IPv6 all-ones mask.
+	*/
+	{
+	// The old QOSLIB expects flow specific events have PF_QOS Message blocks
+	// src, dst and sel filled from the policy selector that the flow is using.
+	// This is very dubious, but at this point fixing the old QOSLIB is not
+	// sensible. Because FLOW SPECIFIC OQS can only be requested through the
+	// QOSLIB, implement a special backward compatibility for this, if
+	// "selector" is located.
+	const CSelectorBase* selector = NULL;
+	if (iChannel == NULL)
+		if ((selector = Policies().iPolicy) == NULL)
+			selector = Policies().iDefault;
+	
+	aMsk.PrefixMask(128);	// All ones IPv6 address.
+
+	aDst.pfqos_address_len = ((sizeof(pfqos_address) + sizeof(TInetAddr) + sizeof(TInetAddr) + 7) / 8);
+	aDst.pfqos_ext_type = EPfqosExtDstAddress;
+	aDst.reserved = 0;
+	if (selector)
+		{
+		// For QOSLIB Only
+		aDst.pfqos_port_max = selector->iDstPortMax;
+		aMsg.iDstAddr.iAddr = &selector->iDst;
+		aMsg.iDstAddr.iPrefix = &selector->iDstMask;
+		}
+	else
+		{
+		aDst.pfqos_port_max = Context().RemotePort();
+		aMsg.iDstAddr.iAddr = &Context().RemoteAddr();
+		aMsg.iDstAddr.iPrefix = &aMsk;
+		}
+	aMsg.iDstAddr.iExt = &aDst;
+
+	aSrc.pfqos_address_len = ((sizeof(pfqos_address) + sizeof(TInetAddr) + sizeof(TInetAddr) + 7) / 8);
+	aSrc.pfqos_ext_type = EPfqosExtSrcAddress;
+	aSrc.reserved = 0;
+	if (selector)
+		{
+		// For QOSLIB Only
+		aSrc.pfqos_port_max = selector->iSrcPortMax;;
+		aMsg.iSrcAddr.iAddr = &selector->iSrc;
+		aMsg.iSrcAddr.iPrefix = &selector->iSrcMask;
+		}
+	else
+		{
+		aSrc.pfqos_port_max = Context().LocalPort();
+		aMsg.iSrcAddr.iAddr = &Context().LocalAddr();
+		aMsg.iSrcAddr.iPrefix = &aMsk;
+		}
+	aMsg.iSrcAddr.iExt = &aSrc;
+
+	if (selector)
+		{
+		// For QOSLIB Only
+		// Brutally run the new constructor for T_pfqos_selector on aSel.
+		// (Need to typecast "const" away from selector -- T_pfqos_selector() is misdeclared, argument should be const!)		
+		new (&aSel) T_pfqos_selector((CSelectorBase *)selector);
+		}
+	else
+		{
+		aSel.pfqos_selector_len = ((sizeof(pfqos_selector) + 7) / 8);
+		aSel.pfqos_ext_type = EPfqosExtSelector;
+		aSel.protocol = (TUint16)Context().Protocol();
+		aSel.uid1 = Uid().UidType()[0].iUid;
+		aSel.uid2 = Uid().UidType()[1].iUid;
+		aSel.uid3 = Uid().UidType()[2].iUid;
+			aSel.iap_id = Interface() ? Interface()->IapId() : 0;
+		aSel.policy_type = EPfqosFlowspecPolicy;
+		aSel.priority = EPfqosApplicationPriority;
+		aSel.reserved = 0;
+		aSel.name[0] = 0;
+		}
+	aMsg.iSelector.iExt = &aSel;
+	}
+
+//
+// ReadyL() is called when a flow comes to EFlow_READY-state. 
+//
+TInt CFlowHook::ReadyL(TPacketHead &/*aHead*/)
+	{
+	// If there is a session going on, the flow is not ready to go!
+	LOG(Log::Printf(_L("")));
+	if (iSession)
+		{
+		LOG(Log::Printf(_L("ReadyL\tqos HOOK[%u] on FLOW[%u] pending on session[%u]"), (TInt)this, (TInt)&iContext, (TInt)iSession));
+		return EFlow_PENDING;
+		}
+
+	LOG(Log::Printf(_L("ReadyL\tqos HOOK[%u] on FLOW[%u]"), (TInt)this, (TInt)&iContext));
+	CNifIfBase *const nif = iContext.Interface();
+	if (iInterface && nif != iInterface->Nif())
+		{
+		// ReadyL can be called multiple times. If the interface has
+		// changed, everything must be started from the beginning...
+		CloseQoS();
+		}
+
+	// Set interface and start QoS negotiation
+
+	// When ReadyL is called, then if iContext.Interface() is normally a non-NULL
+	// pointer to the attached NIF (CNifIfBase). However, the stack architecture allows
+	// also that the final connect could be virtual interface without any NIF. Because such
+	// connect requires a special hook to handle the packets, there are no known uses
+	// of such at this point. However, the QoS code is not prepared to handle NULL NIF
+	// properly and should not do anything with such flows.
+	if (nif == NULL)
+		{
+		ASSERT(iInterface == NULL); // Logically interface cannot be non-NULL here!
+		// ...just return READY and assume rest of the QoS does not panic
+		// on iInterface being NULL. [Note: currently there are no known
+		// configurations in use, which would lead into this branch]
+		return EFlow_READY;
+		}
+
+	if (iInterface == NULL)
+		{
+		//?? Combine FindInterface and AddInterfaceL into single LocateInterfaceL?
+		iInterface = iProtocol.InterfaceMgr()->FindInterface(nif);
+		if (!iInterface)
+			{
+			iInterface = iProtocol.InterfaceMgr()->AddInterfaceL(nif);
+			}
+		ASSERT(iInterface);			// ...at this point, the interface must be present.
+		// Find policies and load modules
+
+		// Attach channel if flow belongs to such.
+		iChannel = iProtocol.ChannelMgr()->AttachChannel(this);
+
+		// Load modules specified in QoS policy
+		LoadModulesL();
+
+		// Open flow in modules (interface must be set before this!!)
+		OpenModulesL();
+
+		UpdateQoS();		// does nothing if iChannel!
+		}
+		
+	if (iChannel)
+		{
+		// Notify Channel
+		TInt ret = iChannel->HookReadyL(*this);
+		if (ret != 0)
+			{
+			LOG(Log::Printf(_L("\tqos HOOK[%u] on FLOW[%u] ReadyL returns=%d (channel)"), (TInt)this, (TInt)&iContext, ret));
+			return ret;	// Flow not ready or has some fatal error...
+			}
+		}
+	else if (iQoSChanged)
+		{
+		// Pending QoS change, try to start negotiation (ignore errors)
+		(void)Negotiate();
+		}
+#ifdef _LOG
+	if (iSession)
+		Log::Printf(_L("\tqos HOOK[%u] on FLOW[%u] ReadyL Returns=1 pending session [%u]"),
+			(TInt)this, (TInt)&iContext, (TInt)iSession);
+	else
+		Log::Printf(_L("\tqos HOOK[%u] on FLOW[%u] ReadyL Returns iFlowStatus=%d"),
+			(TInt)this, (TInt)&iContext, iFlowStatus);
+#endif
+	return iSession ? EFlow_PENDING : iFlowStatus;
+	}
+
+
+
+TInt CFlowHook::ApplyL(RMBufSendPacket &aPacket, RMBufSendInfo &aInfo)
+	{
+	LOG(Log::Printf(_L("")));
+	LOG(Log::Printf(_L("ApplyL\tqos HOOK[%u] on FLOW[%u]"), (TInt)this, (TInt)&iContext));
+
+	const TInt N = iModuleList.Count();
+	//?? Note: ApplyL of the TrafficControl module has not been called before,
+	//?? so keep that semantics unchanged. But, is it actually correct by spec?
+	//?? (if present, control module is at index 0)
+	for (TInt i = iHasControlModule ? 1 : 0; i < N; ++i)
+		{
+		iModuleList[i]->Module()->ApplyL(aPacket, aInfo);
+		}
+	return KErrNone;
+	}
+
+TInt CFlowHook::Negotiate()
+	{
+	if (!Interface() || iChannel || iQoSChanged == 0)
+		{
+		return KErrGeneral;
+		}
+
+	// Check if any modules are associated to a policy
+	// Send reply msg indicating that no modules are associated to a QoS 
+	// policy
+	if (iModuleList.Count() == 0)
+		{
+		iProtocol.Event(KPfqosEventFailure, *this, iQoS, EQoSNoModules);
+		return KErrNone;
+		}
+
+	// only one QoS negotiation can be active at once
+	if (iSession == NULL)
+		{
+		iQoSChanged = 0;
+		return StartPendingRequest(new CNegotiateSession(*this));
+		}
+	// Busy negotiating something else, start this later...
+	return KErrNone;
+	}
+	
+void CFlowHook::LoadModulesL()
+	/**
+	* Load modules specified in a QoS policy.
+	*/
+	{
+	ASSERT(iModuleList.Count() == 0);	// The list must be empty before this!
+	ASSERT(!iHasControlModule);			// The list is empty, there cannot be control module either!
+
+	RModule* rmodule=NULL;
+	if (iInterface->TrafficControl())
+		{
+		// Interface already has a control module. Make a copy reference
+		// to the same module and add to the slot 0 in the module list.
+		rmodule = new (ELeave) RModule(*iInterface->TrafficControl());
+		if (iModuleList.Append(rmodule) != KErrNone)
+			{
+			delete rmodule;
+			User::Leave(KErrNoMemory);
+			}
+		iHasControlModule = TRUE;
+		}
+
+	Policies().iModules = (CModuleSelector*)iProtocol.PolicyMgr()->FindPolicy(&Context(),
+			EPfqosModulespecPolicy, Uid().UidType(), 
+			Interface()->IapId(), EPfqosApplicationPriority, Interface()->InterfaceName());
+
+	CModuleSelector*const sel = Policies().iModules;
+	if (!sel)
+		{
+		// No other modules.
+		return;
+		}
+	TDblQueIter<CModuleSpec> iter(sel->GetModuleList());
+	CModuleSpec *module;
+	while ((module = iter++) != NULL)
+		{
+		TRAPD(err, rmodule = iProtocol.ModuleMgr()->LoadModuleL(*module, iProtocol.NetworkService()->Protocol()));
+		if (err == KErrNone)
+			{
+			if (rmodule->Flags() & KQoSModuleBindToInterface)
+				{
+				// If module has to be binded to an interface, the CNifIfBase
+				// Name must match with the name specified in modulespec 
+				// selector.
+				if (iHasControlModule || sel->iName.Compare(iInterface->InterfaceName()) != 0)
+					{
+					//coverity[leave_without_push]
+					delete rmodule;
+					}
+				else
+					{
+					iInterface->SetTrafficControl(rmodule);
+					LOG(Log::Printf(_L("\t\tCalling module[%S]::InterfaceAttached(IF [%S])"),
+						&rmodule->Name(), &iInterface->Name()));
+					rmodule->Module()->InterfaceAttached(iInterface->Name(), iInterface->Nif());
+					// Make a copy of the control module reference and place it at index 0.
+					rmodule = new (ELeave) RModule(*rmodule);
+					if (iModuleList.Insert(rmodule, 0) != KErrNone)
+						{
+						delete rmodule;
+						User::Leave(KErrNoMemory);
+						}
+					iHasControlModule = TRUE;
+					}
+				}
+			else
+				{
+				if (iModuleList.Append(rmodule) != KErrNone)
+					{
+					delete rmodule;
+					}
+				}
+			}
+		}
+	}
+
+
+void CFlowHook::OpenModulesL()
+	{
+	ASSERT(Interface() != NULL);
+
+	const TInt N = iModuleList.Count();
+	for (TInt i = 0; i < N; ++i)
+		{
+		RModule* const module = iModuleList[i];
+		LOG(Log::Printf(_L("")));
+		LOG(Log::Printf(_L("calling\tmodule[%S]::OpenL(FLOW[%u]) for HOOK[%u] on IF [%S])"),
+			&module->Name(), (TInt)&iContext, (TInt)this, &iInterface->Name()));
+		module->Module()->OpenL(iContext, Interface()->Nif());
+		LOG(Log::Printf(_L("<<<\treturns from module[%S] for HOOK[%u]"), &module->Name(), (TInt)this));
+		}
+	//?? If this leaves half way, someone should close the modules
+	//?? that were succesfully opened...? [the flow close will do this,
+	//?? but to all modules in the list]
+	}
+
+void CFlowHook::CloseQoS()
+	/**
+	* Close the QoS setup for a flow.
+	*
+	* Cancel QoS done by the ReadyL. Flow has no QoS resources
+	* attached after this.
+	*/	
+	{
+	// Note: iSession is self destructive object, which automaticly destructs
+	// after the ClearPendingRequest call. If it has not done that yet, force
+	// the destruction here (the session destructor must deal with any pending
+	// uncompleted actions in some way!) and not call ClearPendingRequest
+	// any more.
+	delete iSession;
+	iSession = NULL;
+
+	for (TInt i = iModuleList.Count(); --i >= 0; )
+		{
+		RModule* const module = iModuleList[i];
+
+		LOG(Log::Printf(_L("")));
+		LOG(Log::Printf(_L("calling\tmodule[%S]::Close(FLOW[%u]) for HOOK[%u]"), &module->Name(), (TInt)&iContext, (TInt)this));
+		module->Module()->Close(Context());
+		LOG(Log::Printf(_L("<<<\treturns from module[%S] for HOOK[%u]"), &module->Name(), (TInt)this));
+		delete module; 
+		}
+	iModuleList.Reset();
+	iHasControlModule = FALSE;
+
+	// Close channel after Close() to QoS modules
+	if (iChannel)
+		{
+		iChannel->Detach(*this);
+		iChannel = NULL;
+		}
+	
+	iInterface = NULL;
+	}
+
+void CFlowHook::RestartQoS()
+	/**
+	* Restart the QoS for the flow.
+	*
+	* This function closes the current QoS framework assigned to the
+	* flow and requests a rerun of the ReadyL phase.
+	*
+	* This should be called when the flow is moved between channel
+	* and other QoS modes...
+	*/
+	{
+	LOG(Log::Printf(_L("\tqos HOOK[%u] on FLOW[%u] RestartQoS"), (TInt)this, (TInt)&iContext));
+	CloseQoS();
+	// Force immeadiate call to ReadyL by poking the flow status.
+	iContext.SetStatus(EFlow_PENDING);
+	LOG(Log::Printf(_L(">>>\tCalling FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+	iFlowStatus = EFlow_READY;
+	iContext.SetStatus(EFlow_READY);
+	LOG(Log::Printf(_L("<<<\tReturns FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+
+	//?? The above is somewhat inconvenient, as the SetStatus(EFlow_READY) can
+	//?? directly call ReadyL again. Should be careful to avoid infinite
+	//?? recursive loops -- it would be better to have a way to trigger
+	//?? this in delayed way: either implement a callback here, or hook API
+	//?? should have some feature to trigger it.
+	}
+
+
+
+void CFlowHook::ReleasePolicy(const CPolicySelector* aSel)
+	/**
+	* The selector has been deleted, remove references
+	*/
+	{
+	ASSERT(aSel);	// should not be called with NULL ...
+	if (aSel == NULL)
+		return;		// ...but, if it happens, do nothing!
+
+	TInt was_used = 0;
+	if (Policies().iPolicy == aSel)
+		{
+		Policies().iPolicy = NULL;
+		was_used = 1;
+		}
+	if (Policies().iDefault == aSel)
+		{
+		Policies().iDefault = NULL;
+		was_used = 1;
+		}
+	if (Policies().iOverride == aSel)
+		{
+		Policies().iOverride = NULL;
+		was_used = 1;
+		}
+	if (was_used)
+		UpdateQoS();
+	}
+
+void CFlowHook::UpdateQoS()
+	{
+	// update QoS policy if flow does not belong to a QoS channel
+	if (iChannel)
+		return;
+	
+	Policies().iDefault = (CPolicySelector*)iProtocol.PolicyMgr()->FindPolicy(&Context(),
+		EPfqosFlowspecPolicy, Uid().UidType(), 
+		Interface()->IapId(), EPfqosDefaultPriority);
+	Policies().iPolicy = (CPolicySelector*)iProtocol.PolicyMgr()->FindPolicy(&Context(), 
+		EPfqosFlowspecPolicy, Uid().UidType(), 
+		Interface()->IapId(), EPfqosApplicationPriority);
+
+	//
+	// Combine default and application policies
+	//
+	TQoSParameters spec;
+    //lint -e{961} Missing 'else' is OK
+	if (Policies().iDefault && Policies().iPolicy)
+		{
+		spec = Policies().iPolicy->QoSParameters();
+		TPolicyCombine::CombineDefault(Policies().iDefault->QoSParameters(), spec);
+		}
+	else if (Policies().iDefault)
+		{
+		spec = Policies().iDefault->QoSParameters();
+		}
+	else if (Policies().iPolicy)
+		{
+		spec = Policies().iPolicy->QoSParameters();
+		}
+
+	Policies().iOverride = (CPolicySelector*)iProtocol.PolicyMgr()->FindPolicy(&Context(),
+		EPfqosFlowspecPolicy, Uid().UidType(), 
+		Interface()->IapId(), EPfqosOverridePriority);
+	//
+	// Combine application and override policies
+	if (Policies().iOverride && Policies().iPolicy)
+		{
+		(void)TPolicyCombine::CombineOverride(Policies().iOverride->QoSParameters(), spec);
+		}
+
+	AdjustForHeaderMode(spec);
+	if (!(spec == iQoS))
+		{
+		SetQoS(spec);
+		iQoSChanged = 1;
+
+		// Force immeadiate call to ReadyL by poking the flow status
+		// (if it looks probable that negotiation can be started)
+		if (iFlowStatus == EFlow_READY && iSession == NULL)
+			{
+			//?? This really should be done by some callback mechanism (can generate
+			//?? very long call chains -- the latter SetStatus may call ReadyL!)
+			iContext.SetStatus(EFlow_PENDING);
+			LOG(Log::Printf(_L(">>>\tCalling FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+			iContext.SetStatus(EFlow_READY);
+			LOG(Log::Printf(_L("<<<\tReturns FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+			}
+		}
+	}
+
+
+void CFlowHook::SetQoS(const TQoSParameters& aSpec)
+	{
+	iQoS = aSpec;
+	AdjustForHeaderMode(iQoS);
+	}
+
+TInt CFlowHook::StartPendingRequest(CQoSSessionBase* aSession)
+	/**
+	* Start pending request and own the session.
+	*
+	* Starts the pending request and until it completes, owns
+	* the object. The ClearPendingRequest is an indication of
+	* the end of the ownership (session self destructs).
+	*
+	* @return KErrNoMemory, if aSession == NULL (allow to use the new action as parameter)
+	*/
+	{
+	delete iSession;			// (If there is another session, just scrap it).
+	iSession = aSession;
+	if (iSession == NULL)
+		return KErrNoMemory;
+	iSession->Run();
+	return KErrNone;
+	}
+
+void CFlowHook::ClearPendingRequest(TInt /*aResult*/)
+	/**
+	* A pending request has completed.
+	* @param aResult The completion result
+	*/
+	{
+	ASSERT(iSession != NULL);		// Should never get here without a session!
+	if (iSession)
+		{
+		iSession = NULL;
+		// The result of negotiation, even if fails, does not affect the
+		// flow, but the flow needs to be woken up.
+		iFlowStatus = EFlow_READY;
+		LOG(Log::Printf(_L(">>>\tCalling FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+		iContext.SetStatus(EFlow_READY);
+		LOG(Log::Printf(_L("<<<\tReturns FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+		}
+	}
+
+void CFlowHook::Block()
+	{
+	iFlowStatus = EFlow_HOLD;
+	LOG(Log::Printf(_L("\tBlocking FLOW[%u]::SetStatus(EFlow_HOLD)"), (TInt)&iContext));
+	iContext.SetStatus(EFlow_HOLD);
+	};
+
+void CFlowHook::UnBlock()
+	{
+	iFlowStatus = EFlow_READY;
+	LOG(Log::Printf(_L(">>>\tUnblocking FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+	iContext.SetStatus(EFlow_READY);
+	LOG(Log::Printf(_L("<<<\tReturns    FLOW[%u]::SetStatus(0)"), (TInt)&iContext));
+	};
+
+
+void CFlowHook::AdjustForHeaderMode(TQoSParameters& aQoS)
+	// This code works only if we are running as topmost ReadyL, just
+	// under the upper layer protocol. In that case, the HeaderSize()
+	// of the CFlowContext includes all overhead caused by other
+	// hooks. It still misses the overhead of
+	// - upper layder protocol
+	// - IP header
+	//
+	// However, getting the correct filter information requires that we
+	// are the last hook. This means that header size estimation only
+	// works now, if there are no other hooks adding headers to the
+	// flow.
+	{
+	if (!aQoS.GetHeaderMode())
+		return;	// All done, nothing else to do.
+
+
+	// Try guessing the current header size
+	TInt headersize = iContext.HeaderSize();
+	switch (iContext.Protocol())
+		{
+	case KProtocolInetIcmp:
+		headersize += TInet6HeaderICMP::MaxHeaderLength();
+		break;
+	
+	case KProtocolInetTcp:
+		headersize += TInet6HeaderTCP::MaxHeaderLength();
+		break;
+	
+	default: // For anything else, just make a guess and use UDP
+	case KProtocolInetUdp:
+		headersize += TInet6HeaderUDP::MaxHeaderLength();
+		break;
+		}
+	// Make an estimate of IP header overhead
+	if (iContext.iHead.ip6.Version() == 4)
+		// Use Min length for IPv4, because IP options are really not used in this system
+		headersize += TInet6HeaderIP4::MinHeaderLength();
+	else
+		headersize += TInet6HeaderIP::MaxHeaderLength();
+	
+	
+	
+	// Adjust parameters for the header overhead
+
+	TInt maxHeaderRate;
+	if (aQoS.GetUpLinkAveragePacketSize() > 0)
+		{
+		maxHeaderRate = (aQoS.GetUplinkBandwidth() + 
+						 aQoS.GetUpLinkMaximumBurstSize()) / 
+						 aQoS.GetUpLinkAveragePacketSize();
+		}
+	else if (aQoS.GetUpLinkMaximumPacketSize() > 0)
+		{
+		maxHeaderRate = (aQoS.GetUplinkBandwidth() + 
+						 aQoS.GetUpLinkMaximumBurstSize()) / 
+						 aQoS.GetUpLinkMaximumPacketSize();
+		}
+	else
+		{
+		maxHeaderRate = 0;
+		}
+	maxHeaderRate *= headersize;
+	LOG(Log::Printf(_L("\tqos HOOK[%u] on FLOW[%u] est-headersize = %d maxHeaderRate=%d"),
+		(TInt)this, (TInt)&iContext, headersize, maxHeaderRate));
+
+
+	if (aQoS.GetUplinkBandwidth() > 0)
+		{
+		aQoS.SetUplinkBandwidth(aQoS.GetUplinkBandwidth() +  maxHeaderRate);
+		}
+	if (aQoS.GetUpLinkMaximumPacketSize() > 0)
+		{
+		aQoS.SetUpLinkMaximumPacketSize(aQoS.GetUpLinkMaximumPacketSize() + headersize);
+		}
+
+	if (aQoS.GetDownlinkBandwidth() > 0)
+		{
+		aQoS.SetDownlinkBandwidth(aQoS.GetDownlinkBandwidth() + maxHeaderRate);
+		}
+	if (aQoS.GetDownLinkMaximumPacketSize() > 0)
+		{
+		aQoS.SetDownLinkMaximumPacketSize(aQoS.GetDownLinkMaximumPacketSize() + headersize);
+		}
+
+	// Constrain the packet size with MTU
+	// (to be exactly correct: one should note that if application will be using larger than MTU
+	// packets, they will get fragmented, and that overhead might need to computed/estimated
+	// here also?)
+	const TInt incoming = iContext.InterfaceRMtu();
+	const TInt outgoing = iContext.InterfaceSMtu();
+	if (aQoS.GetDownLinkMaximumPacketSize() > incoming)
+		aQoS.SetDownLinkMaximumPacketSize(incoming);
+	if (aQoS.GetUpLinkMaximumPacketSize() > outgoing)
+		aQoS.SetUpLinkMaximumPacketSize(outgoing);
+	// Adjusted parameters, no "header mode" any more.
+	aQoS.SetHeaderMode(EFalse);
+
+	return;
+	}