--- /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;
+ }