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-action-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 <algorithm>
34 #include <optional>
35 
36 namespace ns3
37 {
38 
39 NS_LOG_COMPONENT_DEFINE("BlockAckManager");
40 
41 NS_OBJECT_ENSURE_REGISTERED(BlockAckManager);
42 
43 TypeId
45 {
46  static TypeId tid =
47  TypeId("ns3::BlockAckManager")
48  .SetParent<Object>()
49  .SetGroupName("Wifi")
50  .AddConstructor<BlockAckManager>()
51  .AddTraceSource("AgreementState",
52  "The state of the ADDBA handshake",
54  "ns3::BlockAckManager::AgreementStateTracedCallback");
55  return tid;
56 }
57 
59 {
60  NS_LOG_FUNCTION(this);
61 }
62 
64 {
65  NS_LOG_FUNCTION(this);
66 }
67 
68 void
70 {
71  NS_LOG_FUNCTION(this);
72  m_originatorAgreements.clear();
73  m_queue = nullptr;
74 }
75 
77 BlockAckManager::GetAgreementAsOriginator(const Mac48Address& recipient, uint8_t tid) const
78 {
79  NS_LOG_FUNCTION(this << recipient << +tid);
80  if (auto it = m_originatorAgreements.find({recipient, tid}); it != m_originatorAgreements.end())
81  {
82  return std::cref(it->second.first);
83  }
84 
85  return std::nullopt;
86 }
87 
89 BlockAckManager::GetAgreementAsRecipient(const Mac48Address& originator, uint8_t tid) const
90 {
91  NS_LOG_FUNCTION(this << originator << +tid);
92  if (auto it = m_recipientAgreements.find({originator, tid}); it != m_recipientAgreements.end())
93  {
94  return std::cref(it->second);
95  }
96 
97  return std::nullopt;
98 }
99 
100 void
102  const Mac48Address& recipient,
103  bool htSupported)
104 {
105  NS_LOG_FUNCTION(this << reqHdr << recipient << htSupported);
106  const uint8_t tid = reqHdr.GetTid();
107  OriginatorBlockAckAgreement agreement(recipient, tid);
108  agreement.SetStartingSequence(reqHdr.GetStartingSequence());
109  /* For now we assume that originator doesn't use this field. Use of this field
110  is mandatory only for recipient */
111  agreement.SetBufferSize(reqHdr.GetBufferSize());
112  agreement.SetTimeout(reqHdr.GetTimeout());
113  agreement.SetAmsduSupport(reqHdr.IsAmsduSupported());
114  agreement.SetHtSupported(htSupported);
115  if (reqHdr.IsImmediateBlockAck())
116  {
117  agreement.SetImmediateBlockAck();
118  }
119  else
120  {
121  agreement.SetDelayedBlockAck();
122  }
125  recipient,
126  tid,
128  if (auto existingAgreement = GetAgreementAsOriginator(recipient, tid))
129  {
130  NS_ASSERT_MSG(existingAgreement->get().IsReset(),
131  "Existing agreement must be in RESET state");
132  }
133  m_originatorAgreements.insert_or_assign({recipient, tid},
134  std::make_pair(std::move(agreement), PacketQueue{}));
135  m_blockPackets(recipient, tid);
136 }
137 
138 void
140 {
141  NS_LOG_FUNCTION(this << recipient << +tid);
142  auto it = m_originatorAgreements.find({recipient, tid});
143  if (it != m_originatorAgreements.end())
144  {
145  m_originatorAgreements.erase(it);
146  }
147 }
148 
149 void
151  const Mac48Address& recipient,
152  uint16_t startingSeq)
153 {
154  NS_LOG_FUNCTION(this << respHdr << recipient << startingSeq);
155  uint8_t tid = respHdr.GetTid();
156  auto it = m_originatorAgreements.find({recipient, tid});
157  if (it != m_originatorAgreements.end())
158  {
159  OriginatorBlockAckAgreement& agreement = it->second.first;
160  agreement.SetBufferSize(respHdr.GetBufferSize());
161  agreement.SetTimeout(respHdr.GetTimeout());
162  agreement.SetAmsduSupport(respHdr.IsAmsduSupported());
163  agreement.SetStartingSequence(startingSeq);
164  agreement.InitTxWindow();
165  if (respHdr.IsImmediateBlockAck())
166  {
167  agreement.SetImmediateBlockAck();
168  }
169  else
170  {
171  agreement.SetDelayedBlockAck();
172  }
173  if (!it->second.first.IsEstablished())
174  {
176  recipient,
177  tid,
179  }
181  if (agreement.GetTimeout() != 0)
182  {
183  Time timeout = MicroSeconds(1024 * agreement.GetTimeout());
186  this,
187  recipient,
188  tid);
189  }
190  }
191  m_unblockPackets(recipient, tid);
192 }
193 
194 void
196  const Mac48Address& originator,
197  uint16_t startingSeq,
198  bool htSupported,
199  Ptr<MacRxMiddle> rxMiddle)
200 {
201  NS_LOG_FUNCTION(this << respHdr << originator << startingSeq << htSupported << rxMiddle);
202  uint8_t tid = respHdr.GetTid();
203 
204  RecipientBlockAckAgreement agreement(originator,
205  respHdr.IsAmsduSupported(),
206  tid,
207  respHdr.GetBufferSize(),
208  respHdr.GetTimeout(),
209  startingSeq,
210  htSupported);
211  agreement.SetMacRxMiddle(rxMiddle);
212  if (respHdr.IsImmediateBlockAck())
213  {
214  agreement.SetImmediateBlockAck();
215  }
216  else
217  {
218  agreement.SetDelayedBlockAck();
219  }
220 
221  m_recipientAgreements.insert_or_assign({originator, tid}, agreement);
222 }
223 
224 void
226 {
227  NS_LOG_FUNCTION(this << originator << tid);
228 
229  if (auto agreementIt = m_recipientAgreements.find({originator, tid});
230  agreementIt != m_recipientAgreements.end())
231  {
232  // forward up the buffered MPDUs before destroying the agreement
233  agreementIt->second.Flush();
234  m_recipientAgreements.erase(agreementIt);
235  }
236 }
237 
238 void
240 {
241  NS_LOG_FUNCTION(this << *mpdu);
242  NS_ASSERT(mpdu->GetHeader().IsQosData());
243 
244  uint8_t tid = mpdu->GetHeader().GetQosTid();
245  Mac48Address recipient = mpdu->GetHeader().GetAddr1();
246 
247  auto agreementIt = m_originatorAgreements.find({recipient, tid});
248  NS_ASSERT(agreementIt != m_originatorAgreements.end());
249 
250  uint16_t mpduDist =
251  agreementIt->second.first.GetDistance(mpdu->GetHeader().GetSequenceNumber());
252 
253  if (mpduDist >= SEQNO_SPACE_HALF_SIZE)
254  {
255  NS_LOG_DEBUG("Got an old packet. Do nothing");
256  return;
257  }
258 
259  // store the packet and keep the list sorted in increasing order of sequence number
260  // with respect to the starting sequence number
261  auto it = agreementIt->second.second.rbegin();
262  while (it != agreementIt->second.second.rend())
263  {
264  if (mpdu->GetHeader().GetSequenceControl() == (*it)->GetHeader().GetSequenceControl())
265  {
266  NS_LOG_DEBUG("Packet already in the queue of the BA agreement");
267  return;
268  }
269 
270  uint16_t dist =
271  agreementIt->second.first.GetDistance((*it)->GetHeader().GetSequenceNumber());
272 
273  if (mpduDist > dist || (mpduDist == dist && mpdu->GetHeader().GetFragmentNumber() >
274  (*it)->GetHeader().GetFragmentNumber()))
275  {
276  break;
277  }
278 
279  it++;
280  }
281  agreementIt->second.second.insert(it.base(), mpdu);
282  agreementIt->second.first.NotifyTransmittedMpdu(mpdu);
283 }
284 
285 uint32_t
286 BlockAckManager::GetNBufferedPackets(const Mac48Address& recipient, uint8_t tid) const
287 {
288  NS_LOG_FUNCTION(this << recipient << +tid);
289  auto it = m_originatorAgreements.find({recipient, tid});
290  if (it == m_originatorAgreements.end())
291  {
292  return 0;
293  }
294  return it->second.second.size();
295 }
296 
297 void
299 {
300  NS_LOG_FUNCTION(this << +nPackets);
301  m_blockAckThreshold = nPackets;
302 }
303 
306  PacketQueueI mpduIt,
307  MpduStatus status,
308  const OriginatorAgreementsI& it,
309  const Time& now)
310 {
311  NS_LOG_FUNCTION(this << linkId << **mpduIt << +static_cast<uint8_t>(status));
312 
313  if (!(*mpduIt)->IsQueued())
314  {
315  // MPDU is not in the EDCA queue (e.g., its lifetime expired and it was
316  // removed by another method), remove from the queue of in flight MPDUs
317  NS_LOG_DEBUG("MPDU is not stored in the EDCA queue, drop MPDU");
318  return it->second.second.erase(mpduIt);
319  }
320 
321  if (status == ACKNOWLEDGED)
322  {
323  // the MPDU has to be dequeued from the EDCA queue
324  return it->second.second.erase(mpduIt);
325  }
326 
327  const WifiMacHeader& hdr = (*mpduIt)->GetHeader();
328 
329  NS_ASSERT(hdr.GetAddr1() == it->first.first);
330  NS_ASSERT(hdr.IsQosData() && hdr.GetQosTid() == it->first.second);
331 
332  if (it->second.first.GetDistance(hdr.GetSequenceNumber()) >= SEQNO_SPACE_HALF_SIZE)
333  {
334  NS_LOG_DEBUG("Old packet. Remove from the EDCA queue, too");
336  {
337  m_droppedOldMpduCallback(*mpduIt);
338  }
339  m_queue->Remove(*mpduIt);
340  return it->second.second.erase(mpduIt);
341  }
342 
343  std::optional<PacketQueueI> prevIt;
344  if (mpduIt != it->second.second.begin())
345  {
346  prevIt = std::prev(mpduIt);
347  }
348 
349  if (m_queue->TtlExceeded(*mpduIt, now))
350  {
351  // WifiMacQueue::TtlExceeded() has removed the MPDU from the EDCA queue
352  // and fired the Expired trace source, which called NotifyDiscardedMpdu,
353  // which removed this MPDU (and possibly others) from the in flight queue as well
354  NS_LOG_DEBUG("MSDU lifetime expired, drop MPDU");
355  return (prevIt.has_value() ? std::next(prevIt.value()) : it->second.second.begin());
356  }
357 
358  if (status == STAY_INFLIGHT)
359  {
360  // the MPDU has to stay in flight, do nothing
361  return ++mpduIt;
362  }
363 
364  NS_ASSERT(status == TO_RETRANSMIT);
365  (*mpduIt)->GetHeader().SetRetry();
366  (*mpduIt)->ResetInFlight(linkId); // no longer in flight; will be if retransmitted
367 
368  return it->second.second.erase(mpduIt);
369 }
370 
371 void
373 {
374  NS_LOG_FUNCTION(this << linkId << *mpdu);
375  NS_ASSERT(mpdu->GetHeader().IsQosData());
376 
377  Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
378  uint8_t tid = mpdu->GetHeader().GetQosTid();
379 
380  auto it = m_originatorAgreements.find({recipient, tid});
381  NS_ASSERT(it != m_originatorAgreements.end());
382  NS_ASSERT(it->second.first.IsEstablished());
383 
384  it->second.first.NotifyAckedMpdu(mpdu);
385 
386  // remove the acknowledged frame from the queue of outstanding packets
387  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end(); ++queueIt)
388  {
389  if ((*queueIt)->GetHeader().GetSequenceNumber() == mpdu->GetHeader().GetSequenceNumber())
390  {
391  m_queue->DequeueIfQueued({*queueIt});
392  HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, Simulator::Now());
393  break;
394  }
395  }
396 }
397 
398 void
400 {
401  NS_LOG_FUNCTION(this << linkId << *mpdu);
402  NS_ASSERT(mpdu->GetHeader().IsQosData());
403 
404  Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
405  uint8_t tid = mpdu->GetHeader().GetQosTid();
406 
407  auto it = m_originatorAgreements.find({recipient, tid});
408  NS_ASSERT(it != m_originatorAgreements.end());
409  NS_ASSERT(it->second.first.IsEstablished());
410 
411  // remove the frame from the queue of outstanding packets (it will be re-inserted
412  // if retransmitted)
413  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end(); ++queueIt)
414  {
415  if ((*queueIt)->GetHeader().GetSequenceNumber() == mpdu->GetHeader().GetSequenceNumber())
416  {
417  HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, Simulator::Now());
418  break;
419  }
420  }
421 }
422 
423 std::pair<uint16_t, uint16_t>
425  const CtrlBAckResponseHeader& blockAck,
426  const Mac48Address& recipient,
427  const std::set<uint8_t>& tids,
428  size_t index)
429 {
430  NS_LOG_FUNCTION(this << linkId << blockAck << recipient << index);
431 
432  NS_ABORT_MSG_IF(blockAck.IsBasic(), "Basic Block Ack is not supported");
433  NS_ABORT_MSG_IF(blockAck.IsMultiTid(), "Multi-TID Block Ack is not supported");
434 
435  uint8_t tid = blockAck.GetTidInfo(index);
436  // If this is a Multi-STA Block Ack with All-ack context (TID equal to 14),
437  // use the TID passed by the caller.
438  if (tid == 14)
439  {
440  NS_ASSERT(blockAck.GetAckType(index) && tids.size() == 1);
441  tid = *tids.begin();
442  }
443 
444  auto it = m_originatorAgreements.find({recipient, tid});
445  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
446  {
447  return {0, 0};
448  }
449 
450  uint16_t nSuccessfulMpdus = 0;
451  uint16_t nFailedMpdus = 0;
452 
453  if (it->second.first.m_inactivityEvent.IsRunning())
454  {
455  /* Upon reception of a BlockAck frame, the inactivity timer at the
456  originator must be reset.
457  For more details see section 11.5.3 in IEEE802.11e standard */
458  it->second.first.m_inactivityEvent.Cancel();
459  Time timeout = MicroSeconds(1024 * it->second.first.GetTimeout());
460  it->second.first.m_inactivityEvent =
462  }
463 
464  NS_ASSERT(blockAck.IsCompressed() || blockAck.IsExtendedCompressed() || blockAck.IsMultiSta());
465  Time now = Simulator::Now();
466  std::list<Ptr<const WifiMpdu>> acked;
467 
468  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
469  {
470  uint16_t currentSeq = (*queueIt)->GetHeader().GetSequenceNumber();
471  NS_LOG_DEBUG("Current seq=" << currentSeq);
472  if (blockAck.IsPacketReceived(currentSeq, index))
473  {
474  it->second.first.NotifyAckedMpdu(*queueIt);
475  nSuccessfulMpdus++;
476  if (!m_txOkCallback.IsNull())
477  {
478  m_txOkCallback(*queueIt);
479  }
480  acked.emplace_back(*queueIt);
481  queueIt = HandleInFlightMpdu(linkId, queueIt, ACKNOWLEDGED, it, now);
482  }
483  else
484  {
485  ++queueIt;
486  }
487  }
488 
489  // Dequeue all acknowledged MPDUs at once
490  m_queue->DequeueIfQueued(acked);
491 
492  // Remaining outstanding MPDUs have not been acknowledged
493  for (auto queueIt = it->second.second.begin(); queueIt != it->second.second.end();)
494  {
495  // transmission actually failed if the MPDU is inflight only on the same link on
496  // which we received the BlockAck frame
497  auto linkIds = (*queueIt)->GetInFlightLinkIds();
498 
499  if (linkIds.size() == 1 && *linkIds.begin() == linkId)
500  {
501  nFailedMpdus++;
502  if (!m_txFailedCallback.IsNull())
503  {
504  m_txFailedCallback(*queueIt);
505  }
506  queueIt = HandleInFlightMpdu(linkId, queueIt, TO_RETRANSMIT, it, now);
507  continue;
508  }
509 
510  queueIt = HandleInFlightMpdu(linkId, queueIt, STAY_INFLIGHT, it, now);
511  }
512 
513  return {nSuccessfulMpdus, nFailedMpdus};
514 }
515 
516 void
517 BlockAckManager::NotifyMissedBlockAck(uint8_t linkId, const Mac48Address& recipient, uint8_t tid)
518 {
519  NS_LOG_FUNCTION(this << linkId << recipient << +tid);
520 
521  auto it = m_originatorAgreements.find({recipient, tid});
522  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
523  {
524  return;
525  }
526 
527  Time now = Simulator::Now();
528 
529  // remove all packets from the queue of outstanding packets (they will be
530  // re-inserted if retransmitted)
531  for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
532  {
533  // MPDUs that were transmitted on another link shall stay inflight
534  auto linkIds = (*mpduIt)->GetInFlightLinkIds();
535  if (linkIds.count(linkId) == 0)
536  {
537  mpduIt = HandleInFlightMpdu(linkId, mpduIt, STAY_INFLIGHT, it, now);
538  continue;
539  }
540  mpduIt = HandleInFlightMpdu(linkId, mpduIt, TO_RETRANSMIT, it, now);
541  }
542 }
543 
544 void
546 {
547  NS_LOG_FUNCTION(this << *mpdu);
548 
549  if (!mpdu->GetHeader().IsQosData())
550  {
551  NS_LOG_DEBUG("Not a QoS Data frame");
552  return;
553  }
554 
555  if (!mpdu->GetHeader().IsRetry() && !mpdu->IsInFlight())
556  {
557  NS_LOG_DEBUG("This frame has never been transmitted");
558  return;
559  }
560 
561  Mac48Address recipient = mpdu->GetOriginal()->GetHeader().GetAddr1();
562  uint8_t tid = mpdu->GetHeader().GetQosTid();
563  auto it = m_originatorAgreements.find({recipient, tid});
564  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
565  {
566  NS_LOG_DEBUG("No established Block Ack agreement");
567  return;
568  }
569 
570  uint16_t currStartingSeq = it->second.first.GetStartingSequence();
571  if (QosUtilsIsOldPacket(currStartingSeq, mpdu->GetHeader().GetSequenceNumber()))
572  {
573  NS_LOG_DEBUG("Discarded an old frame");
574  return;
575  }
576 
577  // actually advance the transmit window
578  it->second.first.NotifyDiscardedMpdu(mpdu);
579 
580  // remove old MPDUs from the EDCA queue and from the in flight queue
581  // (including the given MPDU which became old after advancing the transmit window)
582  for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
583  {
584  if (it->second.first.GetDistance((*mpduIt)->GetHeader().GetSequenceNumber()) >=
586  {
587  NS_LOG_DEBUG("Dropping old MPDU: " << **mpduIt);
588  m_queue->DequeueIfQueued({*mpduIt});
590  {
591  m_droppedOldMpduCallback(*mpduIt);
592  }
593  mpduIt = it->second.second.erase(mpduIt);
594  }
595  else
596  {
597  break; // MPDUs are in increasing order of sequence number in the in flight queue
598  }
599  }
600 
601  // schedule a BlockAckRequest
602  NS_LOG_DEBUG("Schedule a Block Ack Request for agreement (" << recipient << ", " << +tid
603  << ")");
604 
605  WifiMacHeader hdr;
607  hdr.SetAddr1(recipient);
608  hdr.SetAddr2(mpdu->GetOriginal()->GetHeader().GetAddr2());
609  hdr.SetDsNotTo();
610  hdr.SetDsNotFrom();
611  hdr.SetNoRetry();
612  hdr.SetNoMoreFragments();
613 
614  ScheduleBar(GetBlockAckReqHeader(recipient, tid), hdr);
615 }
616 
617 void
619  uint8_t tid,
620  uint16_t startingSeq)
621 {
622  NS_LOG_FUNCTION(this << originator << tid << startingSeq);
623  auto it = m_recipientAgreements.find({originator, tid});
624  if (it == m_recipientAgreements.end())
625  {
626  return;
627  }
628  it->second.NotifyReceivedBar(startingSeq);
629 }
630 
631 void
633 {
634  NS_LOG_FUNCTION(this << *mpdu);
635  auto originator = mpdu->GetOriginal()->GetHeader().GetAddr2();
636  NS_ASSERT(mpdu->GetHeader().IsQosData());
637  auto tid = mpdu->GetHeader().GetQosTid();
638 
639  auto it = m_recipientAgreements.find({originator, tid});
640  if (it == m_recipientAgreements.end())
641  {
642  return;
643  }
644  it->second.NotifyReceivedMpdu(mpdu);
645 }
646 
648 BlockAckManager::GetBlockAckReqHeader(const Mac48Address& recipient, uint8_t tid) const
649 {
650  NS_LOG_FUNCTION(this << recipient << +tid);
651  auto it = m_originatorAgreements.find({recipient, tid});
652  NS_ASSERT(it != m_originatorAgreements.end());
653 
654  CtrlBAckRequestHeader reqHdr;
655  reqHdr.SetType((*it).second.first.GetBlockAckReqType());
656  reqHdr.SetTidInfo(tid);
657  reqHdr.SetStartingSequence((*it).second.first.GetStartingSequence());
658  return reqHdr;
659 }
660 
661 void
663 {
664  NS_LOG_FUNCTION(this << reqHdr << hdr);
665 
666  uint8_t tid = reqHdr.GetTidInfo();
667 
668  WifiContainerQueueId queueId(WIFI_CTL_QUEUE, WIFI_UNICAST, hdr.GetAddr1(), std::nullopt);
669  auto pkt = Create<Packet>();
670  pkt->AddHeader(reqHdr);
671  Ptr<WifiMpdu> item = nullptr;
672 
673  // if a BAR for the given agreement is present, replace it with the new one
674  while ((item = m_queue->PeekByQueueId(queueId, item)))
675  {
676  if (item->GetHeader().IsBlockAckReq() && item->GetHeader().GetAddr1() == hdr.GetAddr1())
677  {
678  CtrlBAckRequestHeader otherHdr;
679  item->GetPacket()->PeekHeader(otherHdr);
680  if (otherHdr.GetTidInfo() == tid)
681  {
682  auto bar = Create<WifiMpdu>(pkt, hdr, item->GetTimestamp());
683  // replace item with bar
684  m_queue->Replace(item, bar);
685  return;
686  }
687  }
688  }
689 
690  m_queue->Enqueue(Create<WifiMpdu>(pkt, hdr));
691 }
692 
693 const std::list<BlockAckManager::AgreementKey>&
695 {
696  return m_sendBarIfDataQueued;
697 }
698 
699 void
701 {
702  NS_LOG_FUNCTION(this << recipient << tid);
703  // do nothing if the given pair is already in the list
704  if (std::find(m_sendBarIfDataQueued.begin(),
705  m_sendBarIfDataQueued.end(),
706  BlockAckManager::AgreementKey{recipient, tid}) == m_sendBarIfDataQueued.end())
707  {
708  m_sendBarIfDataQueued.emplace_back(recipient, tid);
709  }
710 }
711 
712 void
714 {
715  NS_LOG_FUNCTION(this << recipient << tid);
716  m_sendBarIfDataQueued.remove({recipient, tid});
717 }
718 
719 void
720 BlockAckManager::InactivityTimeout(const Mac48Address& recipient, uint8_t tid)
721 {
722  NS_LOG_FUNCTION(this << recipient << +tid);
723  m_blockAckInactivityTimeout(recipient, tid, true);
724 }
725 
726 void
728  uint8_t tid,
729  uint16_t startingSeq)
730 {
731  NS_LOG_FUNCTION(this << recipient << +tid << startingSeq);
732  auto it = m_originatorAgreements.find({recipient, tid});
733  NS_ASSERT(it != m_originatorAgreements.end());
734  if (!it->second.first.IsEstablished())
735  {
737  recipient,
738  tid,
740  }
741  it->second.first.SetState(OriginatorBlockAckAgreement::ESTABLISHED);
742  it->second.first.SetStartingSequence(startingSeq);
743 }
744 
745 void
747 {
748  NS_LOG_FUNCTION(this << recipient << +tid);
749  auto it = m_originatorAgreements.find({recipient, tid});
750  NS_ASSERT(it != m_originatorAgreements.end());
751  if (!it->second.first.IsRejected())
752  {
754  recipient,
755  tid,
757  }
758  it->second.first.SetState(OriginatorBlockAckAgreement::REJECTED);
759  m_unblockPackets(recipient, tid);
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.IsNoReply())
769  {
771  recipient,
772  tid,
774  }
775  it->second.first.SetState(OriginatorBlockAckAgreement::NO_REPLY);
776  m_unblockPackets(recipient, tid);
777 }
778 
779 void
781 {
782  NS_LOG_FUNCTION(this << recipient << +tid);
783  auto it = m_originatorAgreements.find({recipient, tid});
784  NS_ASSERT(it != m_originatorAgreements.end());
785  if (!it->second.first.IsReset())
786  {
788  recipient,
789  tid,
791  }
792  it->second.first.SetState(OriginatorBlockAckAgreement::RESET);
793 }
794 
795 void
797 {
798  NS_LOG_FUNCTION(this << queue);
799  m_queue = queue;
800 }
801 
802 bool
804 {
805  auto it = m_originatorAgreements.find({recipient, tid});
806  if (it == m_originatorAgreements.end() || !it->second.first.IsEstablished())
807  {
808  // If the inactivity timer has expired, QosTxop::SendDelbaFrame has been called and
809  // has destroyed the agreement, hence we get here and correctly return false
810  return false;
811  }
812 
813  Time now = Simulator::Now();
814 
815  // A BAR needs to be retransmitted if there is at least a non-expired in flight MPDU
816  for (auto mpduIt = it->second.second.begin(); mpduIt != it->second.second.end();)
817  {
818  // remove MPDU if old or with expired lifetime
819  mpduIt = HandleInFlightMpdu(SINGLE_LINK_OP_ID, mpduIt, STAY_INFLIGHT, it, now);
820 
821  if (mpduIt != it->second.second.begin())
822  {
823  // the MPDU has not been removed
824  return true;
825  }
826  }
827 
828  return false;
829 }
830 
831 void
833 {
834  NS_LOG_FUNCTION(this << &callback);
835  m_blockAckInactivityTimeout = callback;
836 }
837 
838 void
840 {
841  NS_LOG_FUNCTION(this << &callback);
842  m_blockPackets = callback;
843 }
844 
845 void
847 {
848  NS_LOG_FUNCTION(this << &callback);
849  m_unblockPackets = callback;
850 }
851 
852 void
854 {
855  m_txOkCallback = callback;
856 }
857 
858 void
860 {
861  m_txFailedCallback = callback;
862 }
863 
864 void
866 {
867  m_droppedOldMpduCallback = callback;
868 }
869 
870 uint16_t
871 BlockAckManager::GetRecipientBufferSize(const Mac48Address& recipient, uint8_t tid) const
872 {
873  uint16_t size = 0;
874  auto it = m_originatorAgreements.find({recipient, tid});
875  if (it != m_originatorAgreements.end())
876  {
877  size = it->second.first.GetBufferSize();
878  }
879  return size;
880 }
881 
882 uint16_t
884 {
885  uint16_t seqNum = 0;
886  auto it = m_originatorAgreements.find({recipient, tid});
887  if (it != m_originatorAgreements.end())
888  {
889  seqNum = it->second.first.GetStartingSequence();
890  }
891  return seqNum;
892 }
893 
894 } // 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 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.
void ScheduleBar(const CtrlBAckRequestHeader &reqHdr, const WifiMacHeader &hdr)
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:438
bool IsNull() const
Check for null implementation.
Definition: callback.h:569
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.
an EUI-48 address
Definition: mac48-address.h:46
Implement the header for management frames of type Add Block Ack request.
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.
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...
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
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:571
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
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.
virtual 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:1350
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:182
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:188
std::tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
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:192
@ WIFI_MAC_CTL_BACKREQ
ns3::Time timeout
uint32_t prev