diff -r 000000000000 -r af10295192d8 networkcontrol/qosfwconfig/qos/src/flowhook.cpp --- /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 +#include +#include +#include +#include + +#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 + +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 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 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; + }