A Discrete-Event Network Simulator
API
block-ack-manager.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009, 2010 MIRKO BANCHI
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Mirko Banchi <mk.banchi@gmail.com>
18  */
19 
20 #include "block-ack-manager.h"
21 
22 #include "ctrl-headers.h"
23 #include "mac-rx-middle.h"
24 #include "mgt-headers.h"
25 #include "qos-utils.h"
26 #include "wifi-mac-queue.h"
27 #include "wifi-tx-vector.h"
28 #include "wifi-utils.h"
29 
30 #include "ns3/log.h"
31 #include "ns3/simulator.h"
32 
33 #include <optional>
34 
35 namespace ns3
36 {
37 
38 NS_LOG_COMPONENT_DEFINE("BlockAckManager");
39 
40 NS_OBJECT_ENSURE_REGISTERED(BlockAckManager);
41 
42 TypeId
44 {
45  static TypeId tid =
46  TypeId("ns3::BlockAckManager")
47  .SetParent<Object>()
48  .SetGroupName("Wifi")
49  .AddConstructor<BlockAckManager>()
50  .AddTraceSource("AgreementState",
51  "The state of the ADDBA handshake",
53  "ns3::BlockAckManager::AgreementStateTracedCallback");
54  return tid;
55 }
56 
58 {
59  NS_LOG_FUNCTION(this);
60 }
61 
63 {
64  NS_LOG_FUNCTION(this);
65 }
66 
67 void
69 {
70  NS_LOG_FUNCTION(this);
71  m_originatorAgreements.clear();
72  m_queue = nullptr;
73 }
74 
76 BlockAckManager::GetAgreementAsOriginator(const Mac48Address& recipient, uint8_t tid) const
77 {
78  NS_LOG_FUNCTION(this << recipient << +tid);
79  if (auto it = m_originatorAgreements.find({recipient, tid}); it != m_originatorAgreements.end())
80  {
81  return std::cref(it->second.first);
82  }
83 
84  return std::nullopt;
85 }
86 
88 BlockAckManager::GetAgreementAsRecipient(const Mac48Address& originator, uint8_t tid) const
89 {
90  NS_LOG_FUNCTION(this << originator << +tid);
91  if (auto it = m_recipientAgreements.find({originator, tid}); it != m_recipientAgreements.end())
92  {
93  return std::cref(it->second);
94  }
95 
96  return std::nullopt;
97 }
98 
99 void
101  const Mac48Address& recipient,
102  bool htSupported)
103 {
104  NS_LOG_FUNCTION(this << reqHdr << recipient << htSupported);
105  const uint8_t tid = reqHdr.GetTid();
106  OriginatorBlockAckAgreement agreement(recipient, tid);
107  agreement.SetStartingSequence(reqHdr.GetStartingSequence());
108  /* For now we assume that originator doesn't use this field. Use of this field
109  is mandatory only for recipient */
110  agreement.SetBufferSize(reqHdr.GetBufferSize());
111  agreement.SetTimeout(reqHdr.GetTimeout());
112  agreement.SetAmsduSupport(reqHdr.IsAmsduSupported());
113  agreement.SetHtSupported(htSupported);
114  if (reqHdr.IsImmediateBlockAck())
115  {
116  agreement.SetImmediateBlockAck();
117  }
118  else
119  {
120  agreement.SetDelayedBlockAck();
121  }
124  recipient,
125  tid,
127  if (auto existingAgreement = GetAgreementAsOriginator(recipient, tid))
128  {
129  NS_ASSERT_MSG(existingAgreement->get().IsReset(),
130  "Existing agreement must be in RESET state");
131  }
132  m_originatorAgreements.insert_or_assign({recipient, tid},
133  std::make_pair(std::move(agreement), PacketQueue{}));
134  m_blockPackets(recipient, tid);
135 }
136 
137 void
139 {
140  NS_LOG_FUNCTION(this << recipient << +tid);
141  auto it = m_originatorAgreements.find({recipient, tid});
142  if (it != m_originatorAgreements.end())
143  {
144  m_originatorAgreements.erase(it);
145  }
146 }
147 
148 void
150  const Mac48Address& recipient,
151  uint16_t startingSeq)
152 {
153  NS_LOG_FUNCTION(this << respHdr << recipient << startingSeq);
154  uint8_t tid = respHdr.GetTid();
155  auto it = m_originatorAgreements.find({recipient, tid});
156  if (it != m_originatorAgreements.end())
157  {
158  OriginatorBlockAckAgreement& agreement = it->second.first;
159  agreement.SetBufferSize(respHdr.GetBufferSize());
160  agreement.SetTimeout(respHdr.GetTimeout());
161  agreement.SetAmsduSupport(respHdr.IsAmsduSupported());
162  agreement.SetStartingSequence(startingSeq);
163  agreement.InitTxWindow();
164  if (respHdr.IsImmediateBlockAck())
165  {
166  agreement.SetImmediateBlockAck();
167  }
168  else
169  {
170  agreement.SetDelayedBlockAck();
171  }
172  if (!it->second.first.IsEstablished())
173  {
175  recipient,
176  tid,
178  }
180  if (agreement.GetTimeout() != 0)
181  {
182  Time timeout = MicroSeconds(1024 * agreement.GetTimeout());
185  this,
186  recipient,
187  tid);
188  }
189  }
190  m_unblockPackets(recipient, tid);
191 }
192 
193 void
195  const Mac48Address& originator,
196  uint16_t startingSeq,
197  bool htSupported,
198  Ptr<MacRxMiddle> rxMiddle)
199 {
200  NS_LOG_FUNCTION(this << respHdr << originator << startingSeq << htSupported << rxMiddle);
201  uint8_t tid = respHdr.GetTid();
202 
203  RecipientBlockAckAgreement agreement(originator,
204  respHdr.IsAmsduSupported(),
205  tid,
206  respHdr.GetBufferSize(),
207  respHdr.GetTimeout(),
208  startingSeq,
209  htSupported);
210  agreement.SetMacRxMiddle(rxMiddle);
211  if (respHdr.IsImmediateBlockAck())
212  {
213  agreement.SetImmediateBlockAck();
214  }
215  else
216  {
217  agreement.SetDelayedBlockAck();
218  }
219 
220  m_recipientAgreements.insert_or_assign({originator, tid}, agreement);
221 }
222 
223 void
225 {
226  NS_LOG_FUNCTION(this << originator << tid);
227 
228  if (auto agreementIt = m_recipientAgreements.find({originator, tid});
229  agreementIt != m_recipientAgreements.end())
230  {
231  // forward up the buffered MPDUs before destroying the agreement
232  agreementIt->second.Flush();
233  m_recipientAgreements.erase(agreementIt);
234  }
235 }
236 
237 void
239 {
240  NS_LOG_FUNCTION(this << *mpdu);
241  NS_ASSERT(mpdu->GetHeader().IsQosData());
242 
243  uint8_t tid = mpdu->GetHeader().GetQosTid();
244  Mac48Address recipient = mpdu->GetHeader().GetAddr1();
245 
246  auto agreementIt = m_originatorAgreements.find({recipient, tid});
247  NS_ASSERT(agreementIt != m_originatorAgreements.end());
248 
249  uint16_t mpduDist =
250  agreementIt->second.first.GetDistance(mpdu->GetHeader().GetSequenceNumber());
251 
252  if (mpduDist >= SEQNO_SPACE_HALF_SIZE)
253  {
254  NS_LOG_DEBUG("Got an old packet. Do nothing");
255  return;
256  }
257 
258  // store the packet and keep the list sorted in increasing order of sequence number
259  // with respect to the starting sequence number
260  auto it = agreementIt->second.second.rbegin();
261  while (it != agreementIt->second.second.rend())
262  {
263  if (mpdu->GetHeader().GetSequenceControl() == (*it)->GetHeader().GetSequenceControl())
264  {
265  NS_LOG_DEBUG("Packet already in the queue of the BA agreement");
266  return;
267  }
268 
269  uint16_t dist =
270  agreementIt->second.first.GetDistance((*it)->GetHeader().GetSequenceNumber());
271 
272  if (mpduDist > dist || (mpduDist == dist && mpdu->GetHeader().GetFragmentNumber() >
273  (*it)->GetHeader().GetFragmentNumber()))
274  {
275  break;
276  }
277 
278  it++;
279  }
280  agreementIt->second.second.insert(it.base(), mpdu);
281  agreementIt->second.first.NotifyTransmittedMpdu(mpdu);
282 }
283 
284 uint32_t
285 BlockAckManager::GetNBufferedPackets(const Mac48Address& recipient, uint8_t tid) const
286 {
287  NS_LOG_FUNCTION(this << recipient << +tid);
288  auto it = m_originatorAgreements.find({recipient, tid});
289  if (it == m_originatorAgreements.end())
290  {
291  return 0;
292  }
293  return it->second.second.size();
294 }
295 
296 void
298 {
299  NS_LOG_FUNCTION(this << +nPackets);
300  m_blockAckThreshold = nPackets;
301 }
302 
305  PacketQueueI mpduIt,
306  MpduStatus status,
307  const OriginatorAgreementsI& it,
308  const Time& now)
309 {
310  NS_LOG_FUNCTION(this << linkId << **mpduIt << +static_cast<uint8_t>(status));
311 
312  if (!(*mpduIt)->IsQueued())
313  {
314  // MPDU is not in the EDCA queue (e.g., its lifetime expired and it was
315  // removed by another method), remove from the queue of in flight MPDUs
316  NS_LOG_DEBUG("MPDU is not stored in the EDCA queue, drop MPDU");
317  return it->second.second.erase(mpduIt);
318  }
319 
320  if (status == ACKNOWLEDGED)
321  {
322  // the MPDU has to be dequeued from the EDCA queue
323  return it->second.second.erase(mpduIt);
324  }
325 
326  const WifiMacHeader& hdr = (*mpduIt)->GetHeader();
327 
328  NS_ASSERT(hdr.GetAddr1() == it->first.first);
329  NS_ASSERT(hdr.IsQosData() && hdr.GetQosTid() == it->first.second);
330 
331  if (it->second.first.GetDistance(hdr.GetSequenceNumber()) >= SEQNO_SPACE_HALF_SIZE)
332  {
333  NS_LOG_DEBUG("Old packet. Remove from the EDCA queue, too");
335  {
336  m_droppedOldMpduCallback(*mpduIt);
337  }
338  m_queue->Remove(*mpduIt);
339  return it->second.second.erase(mpduIt);
340  }
341 
342  std::optional<PacketQueueI> prevIt;
343  if (mpduIt != it->second.second.begin())
344  {
345  prevIt = std::prev(mpduIt);
346  }
347 
348  if (m_queue->TtlExceeded(*mpduIt, now))
349  {
350  // WifiMacQueue::TtlExceeded() has removed the MPDU from the EDCA queue
351  // and fired the Expired trace source, which called NotifyDiscardedMpdu,
352  // which removed this MPDU (and possibly others) from the in flight queue as well
353  NS_LOG_DEBUG("MSDU lifetime expired, drop MPDU");
354  return (prevIt.has_value() ? std::next(prevIt.value()) : it->second.second.begin());
355  }
356 
357  if (status == STAY_INFLIGHT)
358  {
359  // the MPDU has to stay in flight, do nothing
360  return ++mpduIt;
361  }
362 
363  NS_ASSERT(status == TO_RETRANSMIT);
364  (*mpduIt)->GetHeader().SetRetry();
365  (*mpduIt)->ResetInFlight(linkId); // no longer in flight; will be if retransmitted
366 
367  return it->second.second.erase(mpduIt);
368 }
369 
370 void
372 {
373  NS_LOG_FUNCTION(this << linkId << *mpdu);
374  NS_ASSERT(mpdu->GetHeader().IsQosData());
375 
376  Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
377  uint8_t tid = mpdu->GetHeader().GetQosTid();
378 
379  auto it = m_originatorAgreements.find({recipient, tid});
380  NS_ASSERT(it != m_originatorAgreements.end());
381  NS_ASSERT(it->second.first.IsEstablished());
382 
383  it->second.first.NotifyAckedMpdu(mpdu);
384 
385  // remove the acknowledged frame from the queue of outstanding packets
386  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end(); ++queueIt)
387  {
388  if ((*queueIt)->GetHeader().GetSequenceNumber() == mpdu->GetHeader().GetSequenceNumber())
389  {
390  m_queue->DequeueIfQueued({*queueIt});
391  HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, Simulator::Now());
392  break;
393  }
394  }
395 }
396 
397 void
399 {
400  NS_LOG_FUNCTION(this << linkId << *mpdu);
401  NS_ASSERT(mpdu->GetHeader().IsQosData());
402 
403  Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
404  uint8_t tid = mpdu->GetHeader().GetQosTid();
405 
406  auto it = m_originatorAgreements.find({recipient, tid});
407  NS_ASSERT(it != m_originatorAgreements.end());
408  NS_ASSERT(it->second.first.IsEstablished());
409 
410  // remove the frame from the queue of outstanding packets (it will be re-inserted
411  // if retransmitted)
412  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end(); ++queueIt)
413  {
414  if ((*queueIt)->GetHeader().GetSequenceNumber() == mpdu->GetHeader().GetSequenceNumber())
415  {
416  HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, Simulator::Now());
417  break;
418  }
419  }
420 }
421 
422 std::pair<uint16_t, uint16_t>
424  const CtrlBAckResponseHeader& blockAck,
425  const Mac48Address& recipient,
426  const std::set<uint8_t>& tids,
427  size_t index)
428 {
429  NS_LOG_FUNCTION(this << linkId << blockAck << recipient << index);
430 
431  NS_ABORT_MSG_IF(blockAck.IsBasic(), "Basic Block Ack is not supported");
432  NS_ABORT_MSG_IF(blockAck.IsMultiTid(), "Multi-TID Block Ack is not supported");
433 
434  uint8_t tid = blockAck.GetTidInfo(index);
435  // If this is a Multi-STA Block Ack with All-ack context (TID equal to 14),
436  // use the TID passed by the caller.
437  if (tid == 14)
438  {
439  NS_ASSERT(blockAck.GetAckType(index) && tids.size() == 1);
440  tid = *tids.begin();
441  }
442 
443  auto it = m_originatorAgreements.find({recipient, tid});
444  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
445  {
446  return {0, 0};
447  }
448 
449  uint16_t nSuccessfulMpdus = 0;
450  uint16_t nFailedMpdus = 0;
451 
452  if (it->second.first.m_inactivityEvent.IsRunning())
453  {
454  /* Upon reception of a BlockAck frame, the inactivity timer at the
455  originator must be reset.
456  For more details see section 11.5.3 in IEEE802.11e standard */
457  it->second.first.m_inactivityEvent.Cancel();
458  Time timeout = MicroSeconds(1024 * it->second.first.GetTimeout());
459  it->second.first.m_inactivityEvent =
461  }
462 
463  NS_ASSERT(blockAck.IsCompressed() || blockAck.IsExtendedCompressed() || blockAck.IsMultiSta());
464  Time now = Simulator::Now();
465  std::list<Ptr<const WifiMpdu>> acked;
466 
467  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
468  {
469  uint16_t currentSeq = (*queueIt)->GetHeader().GetSequenceNumber();
470  NS_LOG_DEBUG("Current seq=" << currentSeq);
471  if (blockAck.IsPacketReceived(currentSeq, index))
472  {
473  it->second.first.NotifyAckedMpdu(*queueIt);
474  nSuccessfulMpdus++;
475  if (!m_txOkCallback.IsNull())
476  {
477  m_txOkCallback(*queueIt);
478  }
479  acked.emplace_back(*queueIt);
480  queueIt = HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, now);
481  }
482  else
483  {
484  ++queueIt;
485  }
486  }
487 
488  // Dequeue all acknowledged MPDUs at once
489  m_queue->DequeueIfQueued(acked);
490 
491  // Remaining outstanding MPDUs have not been acknowledged
492  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
493  {
494  // transmission actually failed if the MPDU is inflight only on the same link on
495  // which we received the BlockAck frame
496  auto linkIds = (*queueIt)->GetInFlightLinkIds();
497 
498  if (linkIds.size() == 1 && *linkIds.begin() == linkId)
499  {
500  nFailedMpdus++;
501  if (!m_txFailedCallback.IsNull())
502  {
503  m_txFailedCallback(*queueIt);
504  }
505  queueIt = HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, now);
506  continue;
507  }
508 
509  queueIt = HandleInFlightMpdu(linkId, queueIt, STAY_INFLIGHT, it, now);
510  }
511 
512  return {nSuccessfulMpdus, nFailedMpdus};
513 }
514 
515 void
516 BlockAckManager::NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid)
517 {
518  NS_LOG_FUNCTION(this << linkId << recipient << +tid);
519 
520  auto it = m_originatorAgreements.find({recipient, tid});
521  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
522  {
523  return;
524  }
525 
526  Time now = Simulator::Now();
527 
528  // remove all packets from the queue of outstanding packets (they will be
529  // re-inserted if retransmitted)
530  for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
531  {
532  // MPDUs that were transmitted on another link shall stay inflight
533  auto linkIds = (*mpduIt)->GetInFlightLinkIds();
534  if (linkIds.count(linkId) == 0)
535  {
536  mpduIt = HandleInFlightMpdu(linkId, mpduIt, STAY_INFLIGHT, it, now);
537  continue;
538  }
539  mpduIt = HandleInFlightMpdu(linkId, mpduIt, TO_RETRANSMIT, it, now);
540  }
541 }
542 
543 void
545 {
546  NS_LOG_FUNCTION(this << *mpdu);
547 
548  if (!mpdu->GetHeader().IsQosData())
549  {
550  NS_LOG_DEBUG("Not a QoS Data frame");
551  return;
552  }
553 
554  if (!mpdu->GetHeader().IsRetry() && !mpdu->IsInFlight())
555  {
556  NS_LOG_DEBUG("This frame has never been transmitted");
557  return;
558  }
559 
560  Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
561  uint8_t tid = mpdu->GetHeader().GetQosTid();
562  auto it = m_originatorAgreements.find({recipient, tid});
563  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
564  {
565  NS_LOG_DEBUG("No established Block Ack agreement");
566  return;
567  }
568 
569  uint16_t currStartingSeq = it->second.first.GetStartingSequence();
570  if (QosUtilsIsOldPacket(currStartingSeq, mpdu->GetHeader().GetSequenceNumber()))
571  {
572  NS_LOG_DEBUG("Discarded an old frame");
573  return;
574  }
575 
576  // actually advance the transmit window
577  it->second.first.NotifyDiscardedMpdu(mpdu);
578 
579  // remove old MPDUs from the EDCA queue and from the in flight queue
580  // (including the given MPDU which became old after advancing the transmit window)
581  for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
582  {
583  if (it->second.first.GetDistance((*mpduIt)->GetHeader().GetSequenceNumber()) >=
585  {
586  NS_LOG_DEBUG("Dropping old MPDU: " << **mpduIt);
587  m_queue->DequeueIfQueued({*mpduIt});
589  {
590  m_droppedOldMpduCallback(*mpduIt);
591  }
592  mpduIt = it->second.second.erase(mpduIt);
593  }
594  else
595  {
596  break; // MPDUs are in increasing order of sequence number in the in flight queue
597  }
598  }
599 
600  // schedule a BlockAckRequest
601  NS_LOG_DEBUG("Schedule a Block Ack Request for agreement (" << recipient << ", " << +tid
602  << ")");
603  Ptr<Packet> bar = Create<Packet>();
604  bar->AddHeader(GetBlockAckReqHeader(recipient, tid));
605 
606  WifiMacHeader hdr;
608  hdr.SetAddr1(recipient);
609  hdr.SetAddr2(mpdu->GetOriginal()->GetHeader().GetAddr2());
610  hdr.SetDsNotTo();
611  hdr.SetDsNotFrom();
612  hdr.SetNoRetry();
613  hdr.SetNoMoreFragments();
614 
615  ScheduleBar(Create<WifiMpdu>(bar, hdr));
616 }
617 
618 void
620  uint8_t tid,
621  uint16_t startingSeq)
622 {
623  NS_LOG_FUNCTION(this << originator << tid << startingSeq);
624  auto it = m_recipientAgreements.find({originator, tid});
625  if (it == m_recipientAgreements.end())
626  {
627  return;
628  }
629  it->second.NotifyReceivedBar(startingSeq);
630 }
631 
632 void
634 {
635  NS_LOG_FUNCTION(this << *mpdu);
636  auto originator = mpdu->GetOriginal()->GetHeader().GetAddr2();
637  NS_ASSERT(mpdu->GetHeader().IsQosData());
638  auto tid = mpdu->GetHeader().GetQosTid();
639 
640  auto it = m_recipientAgreements.find({originator, tid});
641  if (it == m_recipientAgreements.end())
642  {
643  return;
644  }
645  it->second.NotifyReceivedMpdu(mpdu);
646 }
647 
649 BlockAckManager::GetBlockAckReqHeader(const Mac48Address& recipient, uint8_t tid) const
650 {
651  NS_LOG_FUNCTION(this << recipient << +tid);
652  auto it = m_originatorAgreements.find({recipient, tid});
653  NS_ASSERT(it != m_originatorAgreements.end());
654 
655  CtrlBAckRequestHeader reqHdr;
656  reqHdr.SetType((*it).second.first.GetBlockAckReqType());
657  reqHdr.SetTidInfo(tid);
658  reqHdr.SetStartingSequence((*it).second.first.GetStartingSequence());
659  return reqHdr;
660 }
661 
662 void
664 {
665  NS_LOG_FUNCTION(this << *bar);
666  NS_ASSERT(bar->GetHeader().IsBlockAckReq());
667 
668  CtrlBAckRequestHeader reqHdr;
669  bar->GetPacket()->PeekHeader(reqHdr);
670  uint8_t tid = reqHdr.GetTidInfo();
671 
672  WifiContainerQueueId queueId(WIFI_CTL_QUEUE, bar->GetHeader().GetAddr2(), std::nullopt);
673  Ptr<WifiMpdu> item = nullptr;
674 
675  // if a BAR for the given agreement is present, replace it with the new one
676  while ((item = m_queue->PeekByQueueId(queueId, item)))
677  {
678  if (item->GetHeader().IsBlockAckReq() &&
679  item->GetHeader().GetAddr1() == bar->GetHeader().GetAddr1())
680  {
681  CtrlBAckRequestHeader otherHdr;
682  item->GetPacket()->PeekHeader(otherHdr);
683  if (otherHdr.GetTidInfo() == tid)
684  {
685  // replace item with bar
686  m_queue->Replace(item, bar);
687  return;
688  }
689  }
690  }
691 
692  m_queue->Enqueue(bar);
693 }
694 
695 void
697 {
698  NS_LOG_FUNCTION(this << *muBar);
699  NS_ASSERT(muBar->GetHeader().IsTrigger());
700 
701 #ifdef NS3_BUILD_PROFILE_DEBUG
702  CtrlTriggerHeader triggerHdr;
703  muBar->GetPacket()->PeekHeader(triggerHdr);
704  NS_ASSERT(triggerHdr.IsMuBar());
705 #endif
706 
707  m_queue->Enqueue(muBar);
708 }
709 
710 const std::list<BlockAckManager::AgreementKey>&
712 {
713  return m_sendBarIfDataQueued;
714 }
715 
716 void
718 {
719  NS_LOG_FUNCTION(this << recipient << tid);
720  // do nothing if the given pair is already in the list
721  if (std::find(m_sendBarIfDataQueued.begin(),
722  m_sendBarIfDataQueued.end(),
723  BlockAckManager::AgreementKey{recipient, tid}) == m_sendBarIfDataQueued.end())
724  {
725  m_sendBarIfDataQueued.emplace_back(recipient, tid);
726  }
727 }
728 
729 void
731 {
732  NS_LOG_FUNCTION(this << recipient << tid);
733  m_sendBarIfDataQueued.remove({recipient, tid});
734 }
735 
736 void
737 BlockAckManager::InactivityTimeout(const Mac48Address& recipient, uint8_t tid)
738 {
739  NS_LOG_FUNCTION(this << recipient << +tid);
740  m_blockAckInactivityTimeout(recipient, tid, true);
741 }
742 
743 void
745  uint8_t tid,
746  uint16_t startingSeq)
747 {
748  NS_LOG_FUNCTION(this << recipient << +tid << startingSeq);
749  auto it = m_originatorAgreements.find({recipient, tid});
750  NS_ASSERT(it != m_originatorAgreements.end());
751  if (!it->second.first.IsEstablished())
752  {
754  recipient,
755  tid,
757  }
758  it->second.first.SetState(OriginatorBlockAckAgreement::ESTABLISHED);
759  it->second.first.SetStartingSequence(startingSeq);
760 }
761 
762 void
764 {
765  NS_LOG_FUNCTION(this << recipient << +tid);
766  auto it = m_originatorAgreements.find({recipient, tid});
767  NS_ASSERT(it != m_originatorAgreements.end());
768  if (!it->second.first.IsRejected())
769  {
771  recipient,
772  tid,
774  }
775  it->second.first.SetState(OriginatorBlockAckAgreement::REJECTED);
776 }
777 
778 void
780 {
781  NS_LOG_FUNCTION(this << recipient << +tid);
782  auto it = m_originatorAgreements.find({recipient, tid});
783  NS_ASSERT(it != m_originatorAgreements.end());
784  if (!it->second.first.IsNoReply())
785  {
787  recipient,
788  tid,
790  }
791  it->second.first.SetState(OriginatorBlockAckAgreement::NO_REPLY);
792  m_unblockPackets(recipient, tid);
793 }
794 
795 void
797 {
798  NS_LOG_FUNCTION(this << recipient << +tid);
799  auto it = m_originatorAgreements.find({recipient, tid});
800  NS_ASSERT(it != m_originatorAgreements.end());
801  if (!it->second.first.IsReset())
802  {
804  recipient,
805  tid,
807  }
808  it->second.first.SetState(OriginatorBlockAckAgreement::RESET);
809 }
810 
811 void
813 {
814  NS_LOG_FUNCTION(this << queue);
815  m_queue = queue;
816 }
817 
818 bool
820 {
821  auto it = m_originatorAgreements.find({recipient, tid});
822  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
823  {
824  // If the inactivity timer has expired, QosTxop::SendDelbaFrame has been called and
825  // has destroyed the agreement, hence we get here and correctly return false
826  return false;
827  }
828 
829  Time now = Simulator::Now();
830 
831  // A BAR needs to be retransmitted if there is at least a non-expired in flight MPDU
832  for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
833  {
834  // remove MPDU if old or with expired lifetime
835  mpduIt = HandleInFlightMpdu(SINGLE_LINK_OP_ID, mpduIt, STAY_INFLIGHT, it, now);
836 
837  if (mpduIt != it->second.second.begin())
838  {
839  // the MPDU has not been removed
840  return true;
841  }
842  }
843 
844  return false;
845 }
846 
847 void
849 {
850  NS_LOG_FUNCTION(this << &callback);
851  m_blockAckInactivityTimeout = callback;
852 }
853 
854 void
856 {
857  NS_LOG_FUNCTION(this << &callback);
858  m_blockPackets = callback;
859 }
860 
861 void
863 {
864  NS_LOG_FUNCTION(this << &callback);
865  m_unblockPackets = callback;
866 }
867 
868 void
870 {
871  m_txOkCallback = callback;
872 }
873 
874 void
876 {
877  m_txFailedCallback = callback;
878 }
879 
880 void
882 {
883  m_droppedOldMpduCallback = callback;
884 }
885 
886 uint16_t
887 BlockAckManager::GetRecipientBufferSize(const Mac48Address& recipient, uint8_t tid) const
888 {
889  uint16_t size = 0;
890  auto it = m_originatorAgreements.find({recipient, tid});
891  if (it != m_originatorAgreements.end())
892  {
893  size = it->second.first.GetBufferSize();
894  }
895  return size;
896 }
897 
898 uint16_t
900 {
901  uint16_t seqNum = 0;
902  auto it = m_originatorAgreements.find({recipient, tid});
903  if (it != m_originatorAgreements.end())
904  {
905  seqNum = it->second.first.GetStartingSequence();
906  }
907  return seqNum;
908 }
909 
910 } // namespace ns3
uint16_t GetTimeout() const
Return the timeout.
void SetImmediateBlockAck()
Set block ack policy to immediate Ack.
void SetStartingSequence(uint16_t seq)
Set starting sequence number.
EventId m_inactivityEvent
inactivity event
void SetBufferSize(uint16_t bufferSize)
Set buffer size.
void SetDelayedBlockAck()
Set block ack policy to delayed Ack.
void SetAmsduSupport(bool supported)
Enable or disable A-MSDU support.
void SetTimeout(uint16_t timeout)
Set timeout.
void SetHtSupported(bool htSupported)
Enable or disable HT support.
Manages all block ack agreements for an originator station.
void SetTxFailedCallback(TxFailed callback)
RecipientAgreementOptConstRef GetAgreementAsRecipient(const Mac48Address &originator, uint8_t tid) const
void NotifyOriginatorAgreementEstablished(const Mac48Address &recipient, uint8_t tid, uint16_t startingSeq)
void CreateOriginatorAgreement(const MgtAddBaRequestHeader &reqHdr, const Mac48Address &recipient, bool htSupported=true)
std::pair< Mac48Address, uint8_t > AgreementKey
agreement key typedef (MAC address and TID)
void UpdateOriginatorAgreement(const MgtAddBaResponseHeader &respHdr, const Mac48Address &recipient, uint16_t startingSeq)
Callback< void, Mac48Address, uint8_t > m_unblockPackets
unblock packets callback
OriginatorAgreements::iterator OriginatorAgreementsI
typedef for an iterator for Agreements
void SetQueue(const Ptr< WifiMacQueue > queue)
void DestroyRecipientAgreement(const Mac48Address &originator, uint8_t tid)
Destroy a recipient Block Ack agreement.
void SetTxOkCallback(TxOk callback)
void InactivityTimeout(const Mac48Address &recipient, uint8_t tid)
Inactivity timeout function.
uint8_t m_blockAckThreshold
block ack threshold
std::list< Ptr< WifiMpdu > > PacketQueue
typedef for a list of WifiMpdu.
RecipientAgreements m_recipientAgreements
Recipient Block Ack agreements.
std::optional< std::reference_wrapper< const OriginatorBlockAckAgreement > > OriginatorAgreementOptConstRef
optional const reference to OriginatorBlockAckAgreement
static TypeId GetTypeId()
Get the type ID.
TracedCallback< Time, Mac48Address, uint8_t, OriginatorBlockAckAgreement::State > m_originatorAgreementState
The trace source fired when a state transition occurred.
void SetBlockAckThreshold(uint8_t nPackets)
DroppedOldMpdu m_droppedOldMpduCallback
the dropped MPDU callback
std::optional< std::reference_wrapper< const RecipientBlockAckAgreement > > RecipientAgreementOptConstRef
optional const reference to RecipientBlockAckAgreement
void SetDroppedOldMpduCallback(DroppedOldMpdu callback)
TxFailed m_txFailedCallback
transmit failed callback
void SetUnblockDestinationCallback(Callback< void, Mac48Address, uint8_t > callback)
Set unblock destination callback.
void NotifyOriginatorAgreementRejected(const Mac48Address &recipient, uint8_t tid)
Ptr< WifiMacQueue > m_queue
queue
uint16_t GetRecipientBufferSize(const Mac48Address &recipient, uint8_t tid) const
This function returns the buffer size negotiated with the recipient.
void CreateRecipientAgreement(const MgtAddBaResponseHeader &respHdr, const Mac48Address &originator, uint16_t startingSeq, bool htSupported, Ptr< MacRxMiddle > rxMiddle)
void ScheduleBar(Ptr< WifiMpdu > bar)
void ScheduleMuBar(Ptr< WifiMpdu > muBar)
void NotifyOriginatorAgreementNoReply(const Mac48Address &recipient, uint8_t tid)
void NotifyMissedBlockAck(uint8_t linkId, const Mac48Address &recipient, uint8_t tid)
OriginatorAgreements m_originatorAgreements
This data structure contains, for each originator block ack agreement (recipient, TID),...
void StorePacket(Ptr< WifiMpdu > mpdu)
void NotifyOriginatorAgreementReset(const Mac48Address &recipient, uint8_t tid)
void RemoveFromSendBarIfDataQueuedList(const Mac48Address &recipient, uint8_t tid)
Remove the given (recipient, TID) pair from the list of BA agreements for which a BAR shall only be s...
void NotifyGotAck(uint8_t linkId, Ptr< const WifiMpdu > mpdu)
Invoked upon receipt of an Ack frame on the given link after the transmission of a QoS data frame sen...
void AddToSendBarIfDataQueuedList(const Mac48Address &recipient, uint8_t tid)
Add the given (recipient, TID) pair to the list of BA agreements for which a BAR shall only be sent i...
void NotifyDiscardedMpdu(Ptr< const WifiMpdu > mpdu)
void NotifyGotMpdu(Ptr< const WifiMpdu > mpdu)
bool NeedBarRetransmission(uint8_t tid, const Mac48Address &recipient)
This function returns true if a block ack agreement is established with the given recipient for the g...
void SetBlockDestinationCallback(Callback< void, Mac48Address, uint8_t > callback)
Set block destination callback.
std::list< AgreementKey > m_sendBarIfDataQueued
list of BA agreements for which a BAR shall only be sent if data is queued
std::list< Ptr< WifiMpdu > >::iterator PacketQueueI
typedef for an iterator for PacketQueue.
uint32_t GetNBufferedPackets(const Mac48Address &recipient, uint8_t tid) const
PacketQueueI HandleInFlightMpdu(uint8_t linkId, PacketQueueI mpduIt, MpduStatus status, const OriginatorAgreementsI &it, const Time &now)
Handle the given in flight MPDU based on its given status.
void SetBlockAckInactivityCallback(Callback< void, Mac48Address, uint8_t, bool > callback)
Set block ack inactivity callback.
uint16_t GetOriginatorStartingSequence(const Mac48Address &recipient, uint8_t tid) const
This function returns the starting sequence number of the transmit window.
Callback< void, Mac48Address, uint8_t, bool > m_blockAckInactivityTimeout
BlockAck inactivity timeout callback.
TxOk m_txOkCallback
transmit OK callback
MpduStatus
Enumeration for the statuses of a buffered MPDU.
std::pair< uint16_t, uint16_t > NotifyGotBlockAck(uint8_t linkId, const CtrlBAckResponseHeader &blockAck, const Mac48Address &recipient, const std::set< uint8_t > &tids, size_t index=0)
void DoDispose() override
Destructor implementation.
Callback< void, Mac48Address, uint8_t > m_blockPackets
block packets callback
void DestroyOriginatorAgreement(const Mac48Address &recipient, uint8_t tid)
OriginatorAgreementOptConstRef GetAgreementAsOriginator(const Mac48Address &recipient, uint8_t tid) const
CtrlBAckRequestHeader GetBlockAckReqHeader(const Mac48Address &recipient, uint8_t tid) const
const std::list< AgreementKey > & GetSendBarIfDataQueuedList() const
void NotifyGotBlockAckRequest(const Mac48Address &originator, uint8_t tid, uint16_t startingSeq)
void NotifyMissedAck(uint8_t linkId, Ptr< WifiMpdu > mpdu)
Invoked upon missed reception of an Ack frame on the given link after the transmission of a QoS data ...
Callback template class.
Definition: callback.h:443
bool IsNull() const
Check for null implementation.
Definition: callback.h:572
Headers for BlockAckRequest.
Definition: ctrl-headers.h:52
uint8_t GetTidInfo() const
Return the Traffic ID (TID).
void SetType(BlockAckReqType type)
Set the BlockAckRequest type.
void SetStartingSequence(uint16_t seq)
Set the starting sequence number from the given raw sequence control field.
void SetTidInfo(uint8_t tid)
Set Traffic ID (TID).
Headers for BlockAck response.
Definition: ctrl-headers.h:203
bool IsExtendedCompressed() const
Check if the current BA policy is Extended Compressed Block Ack.
bool IsPacketReceived(uint16_t seq, std::size_t index=0) const
Check if the packet with the given sequence number was acknowledged in this BlockAck response.
uint8_t GetTidInfo(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the TID_INFO subfield of the BA Control fi...
bool IsBasic() const
Check if the current BA policy is Basic Block Ack.
bool IsCompressed() const
Check if the current BA policy is Compressed Block Ack.
bool IsMultiTid() const
Check if the current BA policy is Multi-TID Block Ack.
bool GetAckType(std::size_t index) const
For Multi-STA Block Acks, get the Ack Type subfield of the Per AID TID Info subfield identified by th...
bool IsMultiSta() const
Check if the BlockAck frame variant is Multi-STA Block Ack.
Headers for Trigger frames.
Definition: ctrl-headers.h:945
bool IsMuBar() const
Check if this is a MU-BAR Trigger frame.
an EUI-48 address
Definition: mac48-address.h:46
Implement the header for management frames of type Add Block Ack request.
Definition: mgt-headers.h:1511
uint16_t GetBufferSize() const
Return the buffer size.
uint16_t GetTimeout() const
Return the timeout.
uint8_t GetTid() const
Return the Traffic ID (TID).
uint16_t GetStartingSequence() const
Return the starting sequence number.
bool IsAmsduSupported() const
Return whether A-MSDU capability is supported.
bool IsImmediateBlockAck() const
Return whether the Block Ack policy is immediate Block Ack.
Implement the header for management frames of type Add Block Ack response.
Definition: mgt-headers.h:1642
uint16_t GetBufferSize() const
Return the buffer size.
bool IsAmsduSupported() const
Return whether A-MSDU capability is supported.
uint8_t GetTid() const
Return the Traffic ID (TID).
bool IsImmediateBlockAck() const
Return whether the Block Ack policy is immediate Block Ack.
uint16_t GetTimeout() const
Return the timeout.
A base class which provides memory management and object aggregation.
Definition: object.h:89
Maintains the state and information about transmitted MPDUs with Ack Policy set to Block Ack for an o...
void SetState(State state)
Set the current state.
void InitTxWindow()
Initialize the originator's transmit window by setting its size and starting sequence number equal to...
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:268
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
Maintains the scoreboard and the receive reordering buffer used by a recipient of a Block Ack agreeme...
void SetMacRxMiddle(const Ptr< MacRxMiddle > rxMiddle)
Set the MAC RX Middle to use.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:568
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:199
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
Implements the IEEE 802.11 MAC header.
uint8_t GetQosTid() const
Return the Traffic ID of a QoS header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
uint16_t GetSequenceNumber() const
Return the sequence number of the header.
void SetNoMoreFragments()
Un-set the More Fragment bit in the Frame Control Field.
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
bool IsQosData() const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
void SetNoRetry()
Un-set the Retry bit in the Frame Control field.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1360
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
bool QosUtilsIsOldPacket(uint16_t startingSeq, uint16_t seqNumber)
This function checks if packet with sequence number seqNumber is an "old" packet.
Definition: qos-utils.cc:186
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static constexpr uint16_t SEQNO_SPACE_HALF_SIZE
Size of the half the space of sequence numbers (used to determine old packets)
Definition: wifi-utils.h:136
static constexpr uint8_t SINGLE_LINK_OP_ID
Link ID for single link operations (helps tracking places where correct link ID is to be used to supp...
Definition: wifi-utils.h:140
@ WIFI_MAC_CTL_BACKREQ
std::tuple< WifiContainerQueueType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, Address, TID) identifying a container queue.
ns3::Time timeout
uint32_t prev