899 lines
29 KiB
C++
899 lines
29 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* Copyright (c) 2009, 2010 MIRKO BANCHI
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation;
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Mirko Banchi <mk.banchi@gmail.com>
|
|
*/
|
|
|
|
#include "ns3/log.h"
|
|
#include "ns3/simulator.h"
|
|
#include "block-ack-manager.h"
|
|
#include "wifi-utils.h"
|
|
#include "ctrl-headers.h"
|
|
#include "mgt-headers.h"
|
|
#include "wifi-mac-queue.h"
|
|
#include "qos-utils.h"
|
|
#include "wifi-tx-vector.h"
|
|
|
|
namespace ns3 {
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("BlockAckManager");
|
|
|
|
Bar::Bar ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
Bar::Bar (Ptr<const WifiMacQueueItem> bar, uint8_t tid, bool skipIfNoDataQueued)
|
|
: bar (bar),
|
|
tid (tid),
|
|
skipIfNoDataQueued (skipIfNoDataQueued)
|
|
{
|
|
NS_LOG_FUNCTION (this << *bar << +tid << skipIfNoDataQueued);
|
|
}
|
|
|
|
NS_OBJECT_ENSURE_REGISTERED (BlockAckManager);
|
|
|
|
TypeId
|
|
BlockAckManager::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::BlockAckManager")
|
|
.SetParent<Object> ()
|
|
.SetGroupName ("Wifi")
|
|
.AddConstructor<BlockAckManager> ()
|
|
.AddTraceSource ("AgreementState",
|
|
"The state of the ADDBA handshake",
|
|
MakeTraceSourceAccessor (&BlockAckManager::m_agreementState),
|
|
"ns3::BlockAckManager::AgreementStateTracedCallback")
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
BlockAckManager::BlockAckManager ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
BlockAckManager::~BlockAckManager ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::DoDispose ()
|
|
{
|
|
NS_LOG_FUNCTION (this);
|
|
m_agreements.clear ();
|
|
m_bars.clear ();
|
|
m_queue = nullptr;
|
|
}
|
|
|
|
bool
|
|
BlockAckManager::ExistsAgreement (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
return (m_agreements.find (std::make_pair (recipient, tid)) != m_agreements.end ());
|
|
}
|
|
|
|
bool
|
|
BlockAckManager::ExistsAgreementInState (Mac48Address recipient, uint8_t tid,
|
|
OriginatorBlockAckAgreement::State state) const
|
|
{
|
|
AgreementsCI it;
|
|
it = m_agreements.find (std::make_pair (recipient, tid));
|
|
if (it != m_agreements.end ())
|
|
{
|
|
switch (state)
|
|
{
|
|
case OriginatorBlockAckAgreement::ESTABLISHED:
|
|
return it->second.first.IsEstablished ();
|
|
case OriginatorBlockAckAgreement::PENDING:
|
|
return it->second.first.IsPending ();
|
|
case OriginatorBlockAckAgreement::REJECTED:
|
|
return it->second.first.IsRejected ();
|
|
case OriginatorBlockAckAgreement::NO_REPLY:
|
|
return it->second.first.IsNoReply ();
|
|
case OriginatorBlockAckAgreement::RESET:
|
|
return it->second.first.IsReset ();
|
|
default:
|
|
NS_FATAL_ERROR ("Invalid state for block ack agreement");
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::CreateAgreement (const MgtAddBaRequestHeader *reqHdr, Mac48Address recipient, bool htSupported)
|
|
{
|
|
NS_LOG_FUNCTION (this << reqHdr << recipient << htSupported);
|
|
std::pair<Mac48Address, uint8_t> key (recipient, reqHdr->GetTid ());
|
|
OriginatorBlockAckAgreement agreement (recipient, reqHdr->GetTid ());
|
|
agreement.SetStartingSequence (reqHdr->GetStartingSequence ());
|
|
/* For now we assume that originator doesn't use this field. Use of this field
|
|
is mandatory only for recipient */
|
|
agreement.SetBufferSize (reqHdr->GetBufferSize());
|
|
agreement.SetTimeout (reqHdr->GetTimeout ());
|
|
agreement.SetAmsduSupport (reqHdr->IsAmsduSupported ());
|
|
agreement.SetHtSupported (htSupported);
|
|
if (reqHdr->IsImmediateBlockAck ())
|
|
{
|
|
agreement.SetImmediateBlockAck ();
|
|
}
|
|
else
|
|
{
|
|
agreement.SetDelayedBlockAck ();
|
|
}
|
|
uint8_t tid = reqHdr->GetTid ();
|
|
m_agreementState (Simulator::Now (), recipient, tid, OriginatorBlockAckAgreement::PENDING);
|
|
agreement.SetState (OriginatorBlockAckAgreement::PENDING);
|
|
PacketQueue queue;
|
|
std::pair<OriginatorBlockAckAgreement, PacketQueue> value (agreement, queue);
|
|
if (ExistsAgreement (recipient, tid))
|
|
{
|
|
// Delete agreement if it exists and in RESET state
|
|
NS_ASSERT (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::RESET));
|
|
m_agreements.erase (key);
|
|
}
|
|
m_agreements.insert (std::make_pair (key, value));
|
|
m_blockPackets (recipient, reqHdr->GetTid ());
|
|
}
|
|
|
|
void
|
|
BlockAckManager::DestroyAgreement (Mac48Address recipient, uint8_t tid)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
if (it != m_agreements.end ())
|
|
{
|
|
m_agreements.erase (it);
|
|
//remove scheduled BAR
|
|
for (std::list<Bar>::const_iterator i = m_bars.begin (); i != m_bars.end (); )
|
|
{
|
|
if (i->bar->GetHeader ().GetAddr1 () == recipient && i->tid == tid)
|
|
{
|
|
i = m_bars.erase (i);
|
|
}
|
|
else
|
|
{
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
BlockAckManager::UpdateAgreement (const MgtAddBaResponseHeader *respHdr, Mac48Address recipient,
|
|
uint16_t startingSeq)
|
|
{
|
|
NS_LOG_FUNCTION (this << respHdr << recipient << startingSeq);
|
|
uint8_t tid = respHdr->GetTid ();
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
if (it != m_agreements.end ())
|
|
{
|
|
OriginatorBlockAckAgreement& agreement = it->second.first;
|
|
agreement.SetBufferSize (respHdr->GetBufferSize () + 1);
|
|
agreement.SetTimeout (respHdr->GetTimeout ());
|
|
agreement.SetAmsduSupport (respHdr->IsAmsduSupported ());
|
|
agreement.SetStartingSequence (startingSeq);
|
|
agreement.InitTxWindow ();
|
|
if (respHdr->IsImmediateBlockAck ())
|
|
{
|
|
agreement.SetImmediateBlockAck ();
|
|
}
|
|
else
|
|
{
|
|
agreement.SetDelayedBlockAck ();
|
|
}
|
|
if (!it->second.first.IsEstablished ())
|
|
{
|
|
m_agreementState (Simulator::Now (), recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED);
|
|
}
|
|
agreement.SetState (OriginatorBlockAckAgreement::ESTABLISHED);
|
|
if (agreement.GetTimeout () != 0)
|
|
{
|
|
Time timeout = MicroSeconds (1024 * agreement.GetTimeout ());
|
|
agreement.m_inactivityEvent = Simulator::Schedule (timeout,
|
|
&BlockAckManager::InactivityTimeout,
|
|
this,
|
|
recipient, tid);
|
|
}
|
|
}
|
|
m_unblockPackets (recipient, tid);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::StorePacket (Ptr<WifiMacQueueItem> mpdu)
|
|
{
|
|
NS_LOG_FUNCTION (this << *mpdu);
|
|
NS_ASSERT (mpdu->GetHeader ().IsQosData ());
|
|
|
|
uint8_t tid = mpdu->GetHeader ().GetQosTid ();
|
|
Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
|
|
|
|
AgreementsI agreementIt = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (agreementIt != m_agreements.end ());
|
|
|
|
uint16_t mpduDist = agreementIt->second.first.GetDistance (mpdu->GetHeader ().GetSequenceNumber ());
|
|
|
|
if (mpduDist >= SEQNO_SPACE_HALF_SIZE)
|
|
{
|
|
NS_LOG_DEBUG ("Got an old packet. Do nothing");
|
|
return;
|
|
}
|
|
|
|
// store the packet and keep the list sorted in increasing order of sequence number
|
|
// with respect to the starting sequence number
|
|
PacketQueueI it = agreementIt->second.second.begin ();
|
|
while (it != agreementIt->second.second.end ())
|
|
{
|
|
if (mpdu->GetHeader ().GetSequenceControl () == (*it)->GetHeader ().GetSequenceControl ())
|
|
{
|
|
NS_LOG_DEBUG ("Packet already in the queue of the BA agreement");
|
|
return;
|
|
}
|
|
|
|
uint16_t dist = agreementIt->second.first.GetDistance ((*it)->GetHeader ().GetSequenceNumber ());
|
|
|
|
if (mpduDist < dist ||
|
|
(mpduDist == dist && mpdu->GetHeader ().GetFragmentNumber () < (*it)->GetHeader ().GetFragmentNumber ()))
|
|
{
|
|
break;
|
|
}
|
|
|
|
it++;
|
|
}
|
|
agreementIt->second.second.insert (it, mpdu);
|
|
agreementIt->second.first.NotifyTransmittedMpdu (mpdu);
|
|
mpdu->SetInFlight ();
|
|
}
|
|
|
|
Ptr<const WifiMacQueueItem>
|
|
BlockAckManager::GetBar (bool remove, uint8_t tid, Mac48Address address)
|
|
{
|
|
Time now = Simulator::Now ();
|
|
Ptr<const WifiMacQueueItem> bar;
|
|
// remove all expired MPDUs from the head of the MAC queue, so that
|
|
// BlockAckRequest frames (if needed) are scheduled
|
|
m_queue->IsEmpty ();
|
|
|
|
auto nextBar = m_bars.begin ();
|
|
|
|
while (nextBar != m_bars.end ())
|
|
{
|
|
Mac48Address recipient = nextBar->bar->GetHeader ().GetAddr1 ();
|
|
|
|
if (address != Mac48Address::GetBroadcast () && tid != 8
|
|
&& (!nextBar->bar->GetHeader ().IsBlockAckReq ()
|
|
|| address != recipient || tid != nextBar->tid))
|
|
{
|
|
// we can only return a BAR addressed to the given station and for the given TID
|
|
nextBar++;
|
|
continue;
|
|
}
|
|
if (nextBar->bar->GetHeader ().IsBlockAckReq ())
|
|
{
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, nextBar->tid));
|
|
if (it == m_agreements.end ())
|
|
{
|
|
// BA agreement was torn down; remove this BAR and continue
|
|
nextBar = m_bars.erase (nextBar);
|
|
continue;
|
|
}
|
|
if (nextBar->skipIfNoDataQueued
|
|
&& m_queue->PeekByTidAndAddress (nextBar->tid, recipient) == m_queue->end ())
|
|
{
|
|
// skip this BAR as there is no data queued
|
|
nextBar++;
|
|
continue;
|
|
}
|
|
// remove expired outstanding MPDUs and update the starting sequence number
|
|
for (auto mpduIt = it->second.second.begin (); mpduIt != it->second.second.end (); )
|
|
{
|
|
if (!(*mpduIt)->IsQueued ())
|
|
{
|
|
// the MPDU is no longer in the EDCA queue
|
|
mpduIt = it->second.second.erase (mpduIt);
|
|
continue;
|
|
}
|
|
|
|
WifiMacQueue::ConstIterator queueIt = (*mpduIt)->GetQueueIterator ();
|
|
|
|
if ((*mpduIt)->GetTimeStamp () + m_queue->GetMaxDelay () <= now)
|
|
{
|
|
// MPDU expired
|
|
it->second.first.NotifyDiscardedMpdu (*mpduIt);
|
|
// Remove from the EDCA queue and fire the Expired trace source, but the
|
|
// consequent call to NotifyDiscardedMpdu does nothing (in particular,
|
|
// does not schedule a BAR) because we have advanced the transmit window
|
|
// and hence this MPDU became an old packet
|
|
m_queue->TtlExceeded (queueIt, now);
|
|
mpduIt = it->second.second.erase (mpduIt);
|
|
}
|
|
else
|
|
{
|
|
// MPDUs are typically in increasing order of remaining lifetime
|
|
break;
|
|
}
|
|
}
|
|
// update BAR if the starting sequence number changed
|
|
CtrlBAckRequestHeader reqHdr;
|
|
nextBar->bar->GetPacket ()->PeekHeader (reqHdr);
|
|
if (reqHdr.GetStartingSequence () != it->second.first.GetStartingSequence ())
|
|
{
|
|
reqHdr.SetStartingSequence (it->second.first.GetStartingSequence ());
|
|
Ptr<Packet> packet = Create<Packet> ();
|
|
packet->AddHeader (reqHdr);
|
|
nextBar->bar = Create<const WifiMacQueueItem> (packet, nextBar->bar->GetHeader ());
|
|
}
|
|
}
|
|
|
|
bar = nextBar->bar;
|
|
if (remove)
|
|
{
|
|
m_bars.erase (nextBar);
|
|
}
|
|
break;
|
|
}
|
|
return bar;
|
|
}
|
|
|
|
uint32_t
|
|
BlockAckManager::GetNBufferedPackets (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
if (it == m_agreements.end ())
|
|
{
|
|
return 0;
|
|
}
|
|
return it->second.second.size ();
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetBlockAckThreshold (uint8_t nPackets)
|
|
{
|
|
NS_LOG_FUNCTION (this << +nPackets);
|
|
m_blockAckThreshold = nPackets;
|
|
}
|
|
|
|
BlockAckManager::PacketQueueI
|
|
BlockAckManager::HandleInFlightMpdu (PacketQueueI mpduIt, MpduStatus status,
|
|
const AgreementsI& it, const Time& now)
|
|
{
|
|
NS_LOG_FUNCTION (this << **mpduIt << +static_cast<uint8_t> (status));
|
|
|
|
if (!(*mpduIt)->IsQueued ())
|
|
{
|
|
// MPDU is not in the EDCA queue (e.g., its lifetime expired and it was
|
|
// removed by another method), remove from the queue of in flight MPDUs
|
|
NS_LOG_DEBUG ("MPDU is not stored in the EDCA queue, drop MPDU");
|
|
return it->second.second.erase (mpduIt);
|
|
}
|
|
|
|
if (status == ACKNOWLEDGED)
|
|
{
|
|
// the MPDU has to be dequeued from the EDCA queue
|
|
m_queue->DequeueIfQueued (*mpduIt);
|
|
return it->second.second.erase (mpduIt);
|
|
}
|
|
|
|
WifiMacHeader& hdr = (*mpduIt)->GetHeader ();
|
|
WifiMacQueue::ConstIterator queueIt = (*mpduIt)->GetQueueIterator ();
|
|
|
|
NS_ASSERT (hdr.GetAddr1 () == it->first.first);
|
|
NS_ASSERT (hdr.IsQosData () && hdr.GetQosTid () == it->first.second);
|
|
|
|
if (it->second.first.GetDistance (hdr.GetSequenceNumber ()) >= SEQNO_SPACE_HALF_SIZE)
|
|
{
|
|
NS_LOG_DEBUG ("Old packet. Remove from the EDCA queue, too");
|
|
if (!m_droppedOldMpduCallback.IsNull ())
|
|
{
|
|
m_droppedOldMpduCallback (*queueIt);
|
|
}
|
|
m_queue->Remove (queueIt);
|
|
return it->second.second.erase (mpduIt);
|
|
}
|
|
|
|
auto nextIt = std::next (mpduIt);
|
|
|
|
if (m_queue->TtlExceeded (queueIt, now))
|
|
{
|
|
// WifiMacQueue::TtlExceeded() has removed the MPDU from the EDCA queue
|
|
// and fired the Expired trace source, which called NotifyDiscardedMpdu,
|
|
// which removed this MPDU from the in flight queue as well
|
|
NS_LOG_DEBUG ("MSDU lifetime expired, drop MPDU");
|
|
return nextIt;
|
|
}
|
|
|
|
if (status == STAY_INFLIGHT)
|
|
{
|
|
// the MPDU has to stay in flight, do nothing
|
|
return ++mpduIt;
|
|
}
|
|
|
|
NS_ASSERT (status == TO_RETRANSMIT);
|
|
(*mpduIt)->GetHeader ().SetRetry ();
|
|
(*mpduIt)->ResetInFlight (); // no longer in flight; will be if retransmitted
|
|
|
|
return it->second.second.erase (mpduIt);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyGotAck (Ptr<const WifiMacQueueItem> mpdu)
|
|
{
|
|
NS_LOG_FUNCTION (this << *mpdu);
|
|
NS_ASSERT (mpdu->GetHeader ().IsQosData ());
|
|
|
|
Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
|
|
uint8_t tid = mpdu->GetHeader ().GetQosTid ();
|
|
NS_ASSERT (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED));
|
|
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
|
|
// remove the acknowledged frame from the queue of outstanding packets
|
|
for (auto queueIt = it->second.second.begin (); queueIt != it->second.second.end (); ++queueIt)
|
|
{
|
|
if ((*queueIt)->GetHeader ().GetSequenceNumber () == mpdu->GetHeader ().GetSequenceNumber ())
|
|
{
|
|
HandleInFlightMpdu (queueIt, ACKNOWLEDGED, it, Simulator::Now ());
|
|
break;
|
|
}
|
|
}
|
|
|
|
it->second.first.NotifyAckedMpdu (mpdu);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyMissedAck (Ptr<WifiMacQueueItem> mpdu)
|
|
{
|
|
NS_LOG_FUNCTION (this << *mpdu);
|
|
NS_ASSERT (mpdu->GetHeader ().IsQosData ());
|
|
|
|
Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
|
|
uint8_t tid = mpdu->GetHeader ().GetQosTid ();
|
|
NS_ASSERT (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED));
|
|
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
|
|
// remove the frame from the queue of outstanding packets (it will be re-inserted
|
|
// if retransmitted)
|
|
for (auto queueIt = it->second.second.begin (); queueIt != it->second.second.end (); ++queueIt)
|
|
{
|
|
if ((*queueIt)->GetHeader ().GetSequenceNumber () == mpdu->GetHeader ().GetSequenceNumber ())
|
|
{
|
|
HandleInFlightMpdu (queueIt, TO_RETRANSMIT, it, Simulator::Now ());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<uint16_t,uint16_t>
|
|
BlockAckManager::NotifyGotBlockAck (const CtrlBAckResponseHeader& blockAck, Mac48Address recipient,
|
|
const std::set<uint8_t>& tids, size_t index)
|
|
{
|
|
NS_LOG_FUNCTION (this << blockAck << recipient << index);
|
|
uint16_t nSuccessfulMpdus = 0;
|
|
uint16_t nFailedMpdus = 0;
|
|
|
|
NS_ABORT_MSG_IF (blockAck.IsBasic (), "Basic Block Ack is not supported");
|
|
NS_ABORT_MSG_IF (blockAck.IsMultiTid (), "Multi-TID Block Ack is not supported");
|
|
|
|
uint8_t tid = blockAck.GetTidInfo (index);
|
|
// If this is a Multi-STA Block Ack with All-ack context (TID equal to 14),
|
|
// use the TID passed by the caller.
|
|
if (tid == 14)
|
|
{
|
|
NS_ASSERT (blockAck.GetAckType (index) && tids.size () == 1);
|
|
tid = *tids.begin ();
|
|
}
|
|
if (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
|
|
{
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
|
|
if (it->second.first.m_inactivityEvent.IsRunning ())
|
|
{
|
|
/* Upon reception of a BlockAck frame, the inactivity timer at the
|
|
originator must be reset.
|
|
For more details see section 11.5.3 in IEEE802.11e standard */
|
|
it->second.first.m_inactivityEvent.Cancel ();
|
|
Time timeout = MicroSeconds (1024 * it->second.first.GetTimeout ());
|
|
it->second.first.m_inactivityEvent = Simulator::Schedule (timeout,
|
|
&BlockAckManager::InactivityTimeout,
|
|
this,
|
|
recipient, tid);
|
|
}
|
|
|
|
NS_ASSERT (blockAck.IsCompressed () || blockAck.IsExtendedCompressed () || blockAck.IsMultiSta ());
|
|
Time now = Simulator::Now ();
|
|
|
|
for (auto queueIt = it->second.second.begin (); queueIt != it->second.second.end (); )
|
|
{
|
|
uint16_t currentSeq = (*queueIt)->GetHeader ().GetSequenceNumber ();
|
|
if (blockAck.IsPacketReceived (currentSeq, index))
|
|
{
|
|
it->second.first.NotifyAckedMpdu (*queueIt);
|
|
nSuccessfulMpdus++;
|
|
if (!m_txOkCallback.IsNull ())
|
|
{
|
|
m_txOkCallback (*queueIt);
|
|
}
|
|
queueIt = HandleInFlightMpdu (queueIt, ACKNOWLEDGED, it, now);
|
|
}
|
|
else
|
|
{
|
|
nFailedMpdus++;
|
|
if (!m_txFailedCallback.IsNull ())
|
|
{
|
|
m_txFailedCallback (*queueIt);
|
|
}
|
|
queueIt = HandleInFlightMpdu (queueIt, TO_RETRANSMIT, it, now);
|
|
}
|
|
}
|
|
}
|
|
return {nSuccessfulMpdus, nFailedMpdus};
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyMissedBlockAck (Mac48Address recipient, uint8_t tid)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
if (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
|
|
{
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
Time now = Simulator::Now ();
|
|
|
|
// remove all packets from the queue of outstanding packets (they will be
|
|
// re-inserted if retransmitted)
|
|
for (auto mpduIt = it->second.second.begin (); mpduIt != it->second.second.end (); )
|
|
{
|
|
mpduIt = HandleInFlightMpdu (mpduIt, TO_RETRANSMIT, it, now);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyDiscardedMpdu (Ptr<const WifiMacQueueItem> mpdu)
|
|
{
|
|
NS_LOG_FUNCTION (this << *mpdu);
|
|
|
|
if (!mpdu->GetHeader ().IsQosData ())
|
|
{
|
|
NS_LOG_DEBUG ("Not a QoS Data frame");
|
|
return;
|
|
}
|
|
|
|
if (!mpdu->GetHeader ().IsRetry () && !mpdu->IsInFlight ())
|
|
{
|
|
NS_LOG_DEBUG ("This frame has never been transmitted");
|
|
return;
|
|
}
|
|
|
|
Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
|
|
uint8_t tid = mpdu->GetHeader ().GetQosTid ();
|
|
if (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
|
|
{
|
|
NS_LOG_DEBUG ("No established Block Ack agreement");
|
|
return;
|
|
}
|
|
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
uint16_t currStartingSeq = it->second.first.GetStartingSequence ();
|
|
if (QosUtilsIsOldPacket (currStartingSeq, mpdu->GetHeader ().GetSequenceNumber ()))
|
|
{
|
|
NS_LOG_DEBUG ("Discarded an old frame");
|
|
return;
|
|
}
|
|
|
|
// actually advance the transmit window
|
|
it->second.first.NotifyDiscardedMpdu (mpdu);
|
|
|
|
// remove old MPDUs from the EDCA queue and from the in flight queue
|
|
// (including the given MPDU which became old after advancing the transmit window)
|
|
for (auto mpduIt = it->second.second.begin (); mpduIt != it->second.second.end (); )
|
|
{
|
|
if (it->second.first.GetDistance ((*mpduIt)->GetHeader ().GetSequenceNumber ()) >= SEQNO_SPACE_HALF_SIZE)
|
|
{
|
|
m_queue->DequeueIfQueued (*mpduIt);
|
|
if (!m_droppedOldMpduCallback.IsNull ())
|
|
{
|
|
m_droppedOldMpduCallback (*mpduIt);
|
|
}
|
|
mpduIt = it->second.second.erase (mpduIt);
|
|
}
|
|
else
|
|
{
|
|
break; // MPDUs are in increasing order of sequence number in the in flight queue
|
|
}
|
|
}
|
|
|
|
// schedule a BlockAckRequest
|
|
NS_LOG_DEBUG ("Schedule a Block Ack Request for agreement (" << recipient << ", " << +tid << ")");
|
|
Ptr<Packet> bar = Create<Packet> ();
|
|
bar->AddHeader (GetBlockAckReqHeader (recipient, tid));
|
|
|
|
WifiMacHeader hdr;
|
|
hdr.SetType (WIFI_MAC_CTL_BACKREQ);
|
|
hdr.SetAddr1 (recipient);
|
|
hdr.SetAddr2 (mpdu->GetHeader ().GetAddr2 ());
|
|
hdr.SetAddr3 (mpdu->GetHeader ().GetAddr3 ());
|
|
hdr.SetDsNotTo ();
|
|
hdr.SetDsNotFrom ();
|
|
hdr.SetNoRetry ();
|
|
hdr.SetNoMoreFragments ();
|
|
|
|
ScheduleBar (Create<const WifiMacQueueItem> (bar, hdr));
|
|
}
|
|
|
|
CtrlBAckRequestHeader
|
|
BlockAckManager::GetBlockAckReqHeader (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
|
|
CtrlBAckRequestHeader reqHdr;
|
|
reqHdr.SetType ((*it).second.first.GetBlockAckReqType ());
|
|
reqHdr.SetTidInfo (tid);
|
|
reqHdr.SetStartingSequence ((*it).second.first.GetStartingSequence ());
|
|
return reqHdr;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::ScheduleBar (Ptr<const WifiMacQueueItem> bar, bool skipIfNoDataQueued)
|
|
{
|
|
NS_LOG_FUNCTION (this << *bar);
|
|
NS_ASSERT (bar->GetHeader ().IsBlockAckReq () || bar->GetHeader ().IsTrigger ());
|
|
|
|
uint8_t tid = 0;
|
|
if (bar->GetHeader ().IsBlockAckReq ())
|
|
{
|
|
CtrlBAckRequestHeader reqHdr;
|
|
bar->GetPacket ()->PeekHeader (reqHdr);
|
|
tid = reqHdr.GetTidInfo ();
|
|
}
|
|
#ifdef NS3_BUILD_PROFILE_DEBUG
|
|
else
|
|
{
|
|
CtrlTriggerHeader triggerHdr;
|
|
bar->GetPacket ()->PeekHeader (triggerHdr);
|
|
NS_ASSERT (triggerHdr.IsMuBar ());
|
|
}
|
|
#endif
|
|
Bar request (bar, tid, skipIfNoDataQueued);
|
|
|
|
// if a BAR for the given agreement is present, replace it with the new one
|
|
std::list<Bar>::const_iterator i = m_bars.end ();
|
|
|
|
if (bar->GetHeader ().IsBlockAckReq ())
|
|
{
|
|
for (i = m_bars.begin (); i != m_bars.end (); i++)
|
|
{
|
|
if (i->bar->GetHeader ().IsBlockAckReq ()
|
|
&& i->bar->GetHeader ().GetAddr1 () == bar->GetHeader ().GetAddr1 () && i->tid == tid)
|
|
{
|
|
i = m_bars.erase (i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bar->GetHeader ().IsRetry ())
|
|
{
|
|
m_bars.push_front (request);
|
|
}
|
|
else
|
|
{
|
|
m_bars.insert (i, request);
|
|
}
|
|
}
|
|
|
|
void
|
|
BlockAckManager::InactivityTimeout (Mac48Address recipient, uint8_t tid)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
m_blockAckInactivityTimeout (recipient, tid, true);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyAgreementEstablished (Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid << startingSeq);
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
if (!it->second.first.IsEstablished ())
|
|
{
|
|
m_agreementState (Simulator::Now (), recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED);
|
|
}
|
|
it->second.first.SetState (OriginatorBlockAckAgreement::ESTABLISHED);
|
|
it->second.first.SetStartingSequence (startingSeq);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyAgreementRejected (Mac48Address recipient, uint8_t tid)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
if (!it->second.first.IsRejected ())
|
|
{
|
|
m_agreementState (Simulator::Now (), recipient, tid, OriginatorBlockAckAgreement::REJECTED);
|
|
}
|
|
it->second.first.SetState (OriginatorBlockAckAgreement::REJECTED);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyAgreementNoReply (Mac48Address recipient, uint8_t tid)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
if (!it->second.first.IsNoReply ())
|
|
{
|
|
m_agreementState (Simulator::Now (), recipient, tid, OriginatorBlockAckAgreement::NO_REPLY);
|
|
}
|
|
it->second.first.SetState (OriginatorBlockAckAgreement::NO_REPLY);
|
|
m_unblockPackets (recipient, tid);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::NotifyAgreementReset (Mac48Address recipient, uint8_t tid)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid);
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
if (!it->second.first.IsReset ())
|
|
{
|
|
m_agreementState (Simulator::Now (), recipient, tid, OriginatorBlockAckAgreement::RESET);
|
|
}
|
|
it->second.first.SetState (OriginatorBlockAckAgreement::RESET);
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetQueue (const Ptr<WifiMacQueue> queue)
|
|
{
|
|
NS_LOG_FUNCTION (this << queue);
|
|
m_queue = queue;
|
|
}
|
|
|
|
bool
|
|
BlockAckManager::SwitchToBlockAckIfNeeded (Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
|
|
{
|
|
NS_LOG_FUNCTION (this << recipient << +tid << startingSeq);
|
|
NS_ASSERT (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::PENDING));
|
|
if (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::REJECTED) && ExistsAgreement (recipient, tid))
|
|
{
|
|
uint32_t packets = m_queue->GetNPacketsByTidAndAddress (tid, recipient) +
|
|
GetNBufferedPackets (recipient, tid);
|
|
if (packets >= m_blockAckThreshold)
|
|
{
|
|
NotifyAgreementEstablished (recipient, tid, startingSeq);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool BlockAckManager::NeedBarRetransmission (uint8_t tid, Mac48Address recipient)
|
|
{
|
|
if (ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::ESTABLISHED))
|
|
{
|
|
AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ASSERT (it != m_agreements.end ());
|
|
|
|
Time now = Simulator::Now ();
|
|
|
|
// A BAR needs to be retransmitted if there is at least a non-expired in flight MPDU
|
|
for (auto mpduIt = it->second.second.begin (); mpduIt != it->second.second.end (); )
|
|
{
|
|
// remove MPDU if old or with expired lifetime
|
|
mpduIt = HandleInFlightMpdu (mpduIt, STAY_INFLIGHT, it, now);
|
|
|
|
if (mpduIt != it->second.second.begin ())
|
|
{
|
|
// the MPDU has not been removed
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If the inactivity timer has expired, QosTxop::SendDelbaFrame has been called and
|
|
// has destroyed the agreement, hence we get here and correctly return false
|
|
return false;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetBlockAckInactivityCallback (Callback<void, Mac48Address, uint8_t, bool> callback)
|
|
{
|
|
NS_LOG_FUNCTION (this << &callback);
|
|
m_blockAckInactivityTimeout = callback;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetBlockDestinationCallback (Callback<void, Mac48Address, uint8_t> callback)
|
|
{
|
|
NS_LOG_FUNCTION (this << &callback);
|
|
m_blockPackets = callback;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetUnblockDestinationCallback (Callback<void, Mac48Address, uint8_t> callback)
|
|
{
|
|
NS_LOG_FUNCTION (this << &callback);
|
|
m_unblockPackets = callback;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetTxOkCallback (TxOk callback)
|
|
{
|
|
m_txOkCallback = callback;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetTxFailedCallback (TxFailed callback)
|
|
{
|
|
m_txFailedCallback = callback;
|
|
}
|
|
|
|
void
|
|
BlockAckManager::SetDroppedOldMpduCallback (DroppedOldMpdu callback)
|
|
{
|
|
m_droppedOldMpduCallback = callback;
|
|
}
|
|
|
|
uint16_t
|
|
BlockAckManager::GetRecipientBufferSize (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
uint16_t size = 0;
|
|
AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
if (it != m_agreements.end ())
|
|
{
|
|
size = it->second.first.GetBufferSize ();
|
|
}
|
|
return size;
|
|
}
|
|
|
|
BlockAckReqType
|
|
BlockAckManager::GetBlockAckReqType (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ABORT_MSG_IF (it == m_agreements.end (), "No established Block Ack agreement");
|
|
return it->second.first.GetBlockAckReqType ();
|
|
}
|
|
|
|
BlockAckType
|
|
BlockAckManager::GetBlockAckType (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
NS_ABORT_MSG_IF (it == m_agreements.end (), "No established Block Ack agreement");
|
|
return it->second.first.GetBlockAckType ();
|
|
}
|
|
|
|
uint16_t
|
|
BlockAckManager::GetOriginatorStartingSequence (Mac48Address recipient, uint8_t tid) const
|
|
{
|
|
uint16_t seqNum = 0;
|
|
AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
|
|
if (it != m_agreements.end ())
|
|
{
|
|
seqNum = it->second.first.GetStartingSequence ();
|
|
}
|
|
return seqNum;
|
|
}
|
|
|
|
} //namespace ns3
|