A Discrete-Event Network Simulator
API
ht-frame-exchange-manager.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 Universita' degli Studi di Napoli Federico II
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: Stefano Avallone <stavallo@unina.it>
18  */
19 
21 
22 #include "ns3/abort.h"
23 #include "ns3/ctrl-headers.h"
24 #include "ns3/log.h"
25 #include "ns3/mgt-headers.h"
26 #include "ns3/recipient-block-ack-agreement.h"
27 #include "ns3/snr-tag.h"
28 #include "ns3/wifi-mac-queue.h"
29 #include "ns3/wifi-utils.h"
30 
31 #include <array>
32 #include <optional>
33 
34 #undef NS_LOG_APPEND_CONTEXT
35 #define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
36 
37 namespace ns3
38 {
39 
40 NS_LOG_COMPONENT_DEFINE("HtFrameExchangeManager");
41 
42 NS_OBJECT_ENSURE_REGISTERED(HtFrameExchangeManager);
43 
44 TypeId
46 {
47  static TypeId tid = TypeId("ns3::HtFrameExchangeManager")
49  .AddConstructor<HtFrameExchangeManager>()
50  .SetGroupName("Wifi");
51  return tid;
52 }
53 
55 {
56  NS_LOG_FUNCTION(this);
57  m_msduAggregator = CreateObject<MsduAggregator>();
58  m_mpduAggregator = CreateObject<MpduAggregator>();
59 }
60 
62 {
64 }
65 
66 void
68 {
69  NS_LOG_FUNCTION(this);
70  m_pendingAddBaResp.clear();
71  m_msduAggregator = nullptr;
72  m_mpduAggregator = nullptr;
73  m_psdu = nullptr;
74  m_txParams.Clear();
76 }
77 
78 void
80 {
81  m_msduAggregator->SetWifiMac(mac);
82  m_mpduAggregator->SetWifiMac(mac);
84 }
85 
88 {
89  return m_msduAggregator;
90 }
91 
94 {
95  return m_mpduAggregator;
96 }
97 
100 {
101  return m_mac->GetQosTxop(tid)->GetBaManager();
102 }
103 
104 bool
106 {
107  Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
108  bool establish;
109 
110  if (!GetWifiRemoteStationManager()->GetHtSupported(recipient))
111  {
112  establish = false;
113  }
114  else if (auto agreement = qosTxop->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
115  agreement && !agreement->get().IsReset())
116  {
117  establish = false;
118  }
119  else
120  {
121  WifiContainerQueueId queueId{WIFI_QOSDATA_UNICAST_QUEUE, recipient, tid};
122  uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
123  establish =
124  ((qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
125  (m_mpduAggregator->GetMaxAmpduSize(recipient, tid, WIFI_MOD_CLASS_HT) > 0 &&
126  packets > 1) ||
128  }
129 
130  NS_LOG_FUNCTION(this << recipient << +tid << establish);
131  return establish;
132 }
133 
134 void
136  uint8_t tid,
137  uint16_t startingSeq,
138  uint16_t timeout,
139  bool immediateBAck)
140 {
141  NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck);
142  NS_LOG_DEBUG("Send ADDBA request to " << dest);
143 
144  WifiMacHeader hdr;
146  // use the remote link address if dest is an MLD address
148  hdr.SetAddr1(addr1 ? *addr1 : dest);
149  hdr.SetAddr2(m_self);
150  hdr.SetAddr3(m_bssid);
151  hdr.SetDsNotTo();
152  hdr.SetDsNotFrom();
153 
154  WifiActionHeader actionHdr;
158 
159  Ptr<Packet> packet = Create<Packet>();
160  // Setting ADDBARequest header
161  MgtAddBaRequestHeader reqHdr;
162  reqHdr.SetAmsduSupport(true);
163  if (immediateBAck)
164  {
165  reqHdr.SetImmediateBlockAck();
166  }
167  else
168  {
169  reqHdr.SetDelayedBlockAck();
170  }
171  reqHdr.SetTid(tid);
172  /* For now we don't use buffer size field in the ADDBA request frame. The recipient
173  * will choose how many packets it can receive under block ack.
174  */
175  reqHdr.SetBufferSize(0);
176  reqHdr.SetTimeout(timeout);
177  // set the starting sequence number for the BA agreement
178  reqHdr.SetStartingSequence(startingSeq);
179 
180  GetBaManager(tid)->CreateOriginatorAgreement(reqHdr, dest);
181 
182  packet->AddHeader(reqHdr);
183  packet->AddHeader(actionHdr);
184 
185  Ptr<WifiMpdu> mpdu = Create<WifiMpdu>(packet, hdr);
186 
187  // get the sequence number for the ADDBA Request management frame
188  uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
189  mpdu->GetHeader().SetSequenceNumber(sequence);
190 
191  WifiTxParameters txParams;
192  txParams.m_txVector =
194  txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
195  txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
196 
197  // Wifi MAC queue scheduler is expected to prioritize management frames
198  m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(mpdu);
199  SendMpduWithProtection(mpdu, txParams);
200 }
201 
202 void
204  Mac48Address originator)
205 {
206  NS_LOG_FUNCTION(this << originator);
207  WifiMacHeader hdr;
209  hdr.SetAddr1(originator);
210  hdr.SetAddr2(m_self);
211  hdr.SetAddr3(m_bssid);
212  hdr.SetDsNotFrom();
213  hdr.SetDsNotTo();
214 
215  MgtAddBaResponseHeader respHdr;
216  StatusCode code;
217  code.SetSuccess();
218  respHdr.SetStatusCode(code);
219  // Here a control about queues type?
220  respHdr.SetAmsduSupport(reqHdr->IsAmsduSupported());
221 
222  if (reqHdr->IsImmediateBlockAck())
223  {
224  respHdr.SetImmediateBlockAck();
225  }
226  else
227  {
228  respHdr.SetDelayedBlockAck();
229  }
230  auto tid = reqHdr->GetTid();
231  respHdr.SetTid(tid);
232 
234  respHdr.SetTimeout(reqHdr->GetTimeout());
235 
236  WifiActionHeader actionHdr;
240 
241  Ptr<Packet> packet = Create<Packet>();
242  packet->AddHeader(respHdr);
243  packet->AddHeader(actionHdr);
244 
245  // Get the MLD address of the originator, if an ML setup was performed
246  if (auto originatorMld = GetWifiRemoteStationManager()->GetMldAddress(originator))
247  {
248  originator = *originatorMld;
249  }
250  bool htSupported = GetWifiRemoteStationManager()->GetHtSupported() &&
252  GetBaManager(tid)->CreateRecipientAgreement(respHdr,
253  originator,
254  reqHdr->GetStartingSequence(),
255  htSupported,
256  m_rxMiddle);
257 
258  auto agreement = GetBaManager(tid)->GetAgreementAsRecipient(originator, tid);
259  NS_ASSERT(agreement);
260  if (respHdr.GetTimeout() != 0)
261  {
262  Time timeout = MicroSeconds(1024 * agreement->get().GetTimeout());
263 
264  agreement->get().m_inactivityEvent =
267  this,
268  originator,
269  tid,
270  false);
271  }
272 
273  auto mpdu = Create<WifiMpdu>(packet, hdr);
274 
275  /*
276  * It is possible (though, unlikely) that at this point there are other ADDBA_RESPONSE frame(s)
277  * in the MAC queue. This may happen if the recipient receives an ADDBA_REQUEST frame, enqueues
278  * an ADDBA_RESPONSE frame, but is not able to successfully transmit it before the timer to
279  * wait for ADDBA_RESPONSE expires at the originator. The latter may then send another
280  * ADDBA_REQUEST frame, which triggers the creation of another ADDBA_RESPONSE frame.
281  * To avoid sending unnecessary ADDBA_RESPONSE frames, we keep track of the previously enqueued
282  * ADDBA_RESPONSE frame (if any), dequeue it and replace it with the new ADDBA_RESPONSE frame.
283  */
284 
285  // remove any pending ADDBA_RESPONSE frame
286  AgreementKey key(originator, tid);
287  if (auto it = m_pendingAddBaResp.find(key); it != m_pendingAddBaResp.end())
288  {
289  NS_ASSERT_MSG(it->second, "The pointer to the pending ADDBA_RESPONSE cannot be null");
290  DequeueMpdu(it->second);
291  m_pendingAddBaResp.erase(it);
292  }
293  // store the new ADDBA_RESPONSE frame
294  m_pendingAddBaResp[key] = mpdu;
295 
296  // It is unclear which queue this frame should go into. For now we
297  // bung it into the queue corresponding to the TID for which we are
298  // establishing an agreement, and push it to the head.
299  // Wifi MAC queue scheduler is expected to prioritize management frames
300  m_mac->GetQosTxop(tid)->Queue(mpdu);
301 }
302 
303 uint16_t
305 {
306  return 64;
307 }
308 
309 void
310 HtFrameExchangeManager::SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator)
311 {
312  NS_LOG_FUNCTION(this << addr << +tid << byOriginator);
313  WifiMacHeader hdr;
315  // use the remote link address if addr is an MLD address
316  hdr.SetAddr1(GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr).value_or(addr));
317  hdr.SetAddr2(m_self);
318  hdr.SetAddr3(m_bssid);
319  hdr.SetDsNotTo();
320  hdr.SetDsNotFrom();
321 
322  MgtDelBaHeader delbaHdr;
323  delbaHdr.SetTid(tid);
324  if (byOriginator)
325  {
326  delbaHdr.SetByOriginator();
327  GetBaManager(tid)->DestroyOriginatorAgreement(addr, tid);
328  }
329  else
330  {
331  delbaHdr.SetByRecipient();
332  GetBaManager(tid)->DestroyRecipientAgreement(addr, tid);
333  }
334 
335  WifiActionHeader actionHdr;
339 
340  Ptr<Packet> packet = Create<Packet>();
341  packet->AddHeader(delbaHdr);
342  packet->AddHeader(actionHdr);
343 
344  m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(Create<WifiMpdu>(packet, hdr));
345 }
346 
347 bool
348 HtFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
349 {
350  NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
351 
352  // First, check if there is a BAR to be transmitted
353  if (auto mpdu = GetBar(edca->GetAccessCategory());
354  mpdu && SendMpduFromBaManager(mpdu, availableTime, initialFrame))
355  {
356  return true;
357  }
358 
359  Ptr<WifiMpdu> peekedItem = edca->PeekNextMpdu(m_linkId);
360 
361  // Even though channel access is requested when the queue is not empty, at
362  // the time channel access is granted the lifetime of the packet might be
363  // expired and the queue might be empty.
364  if (!peekedItem)
365  {
366  NS_LOG_DEBUG("No frames available for transmission");
367  return false;
368  }
369 
370  const WifiMacHeader& hdr = peekedItem->GetHeader();
371  // setup a Block Ack agreement if needed
372  if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() &&
373  NeedSetupBlockAck(hdr.GetAddr1(), hdr.GetQosTid()))
374  {
375  // if the peeked MPDU has been already transmitted, use its sequence number
376  // as the starting sequence number for the BA agreement, otherwise use the
377  // next available sequence number
378  uint16_t startingSeq =
379  (hdr.IsRetry()
380  ? hdr.GetSequenceNumber()
381  : m_txMiddle->GetNextSeqNumberByTidAndAddress(hdr.GetQosTid(), hdr.GetAddr1()));
383  hdr.GetQosTid(),
384  startingSeq,
386  true);
387  return true;
388  }
389 
390  // Use SendDataFrame if we can try aggregation
391  if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() && !peekedItem->IsFragment() &&
392  !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem =
393  CreateAliasIfNeeded(peekedItem)))
394  {
395  return SendDataFrame(peekedItem, availableTime, initialFrame);
396  }
397 
398  // Use the QoS FEM to transmit the frame in all the other cases, i.e.:
399  // - the frame is not a QoS data frame
400  // - the frame is a broadcast QoS data frame
401  // - the frame is a fragment
402  // - the frame must be fragmented
403  return QosFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
404 }
405 
408  std::optional<uint8_t> optTid,
409  std::optional<Mac48Address> optAddress)
410 {
411  NS_LOG_FUNCTION(this << +ac << optTid.has_value() << optAddress.has_value());
412  NS_ASSERT_MSG(optTid.has_value() == optAddress.has_value(),
413  "Either both or none of TID and address must be provided");
414 
415  // remove all expired MPDUs from the MAC queue, so that
416  // BlockAckRequest frames (if needed) are scheduled
417  auto queue = m_mac->GetTxopQueue(ac);
418  queue->WipeAllExpiredMpdus();
419 
420  // in case of MLD, we need to check both the queue of control frames that need to be sent
421  // on our link and the queue of control frames that can be sent on any link. We start with
422  // the former, but we check the latter even if a suitable frame is found in the former
423  // (because a BAR that can be sent on any link to a recipient is no longer needed after
424  // sending a BAR to that recipient on our link).
425 
426  std::list<WifiContainerQueueId> queueIds{{WIFI_CTL_QUEUE, m_self, std::nullopt}};
427  if (m_self != m_mac->GetAddress())
428  {
429  // add the MLD address
430  queueIds.emplace_back(WIFI_CTL_QUEUE, m_mac->GetAddress(), std::nullopt);
431  }
432  Ptr<WifiMpdu> selectedBar;
433  uint8_t selectedTid = 0;
434 
435  for (const auto& queueId : queueIds)
436  {
437  Ptr<WifiMpdu> bar;
438  Ptr<WifiMpdu> prevBar;
439 
440  while ((bar = queue->PeekByQueueId(queueId, prevBar)))
441  {
442  if (bar->GetHeader().IsBlockAckReq())
443  {
444  CtrlBAckRequestHeader reqHdr;
445  bar->GetPacket()->PeekHeader(reqHdr);
446  auto tid = reqHdr.GetTidInfo();
447  Mac48Address recipient = bar->GetHeader().GetAddr1();
448 
449  if (selectedBar)
450  {
451  NS_ASSERT_MSG(std::get<Mac48Address>(queueId) != m_self,
452  "We shall not keep iterating over the control frames that need"
453  "to be sent on a specific link after selecting a BAR to send");
454  // if this BlockAckReq is addressed to the same recipient as the selected BAR,
455  // remove it from the queue and stop iterating over this queue; otherwise,
456  // move on to the next frame in the queue
457  if (GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient) ==
458  selectedBar->GetHeader().GetAddr1() &&
459  selectedTid == tid)
460  {
461  queue->Remove(bar);
462  break;
463  }
464  prevBar = bar;
465  continue;
466  }
467 
468  // if this BlockAckReq can be sent on any link, we have to check that this link
469  // has been setup with the recipient (this may happen, e.g., if we are an AP MLD
470  // and we have setup a subset of our links with a non-AP MLD)
471  if (bar->GetHeader().GetAddr2() != m_self && m_mac->GetMldAddress(recipient) &&
472  !GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient))
473  {
474  prevBar = bar;
475  continue;
476  }
477 
478  if (optAddress &&
480  ->GetMldAddress(*optAddress)
481  .value_or(*optAddress) !=
482  GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(
483  recipient) ||
484  optTid != tid))
485  {
486  NS_LOG_DEBUG("BAR " << *bar
487  << " cannot be returned because it is not addressed"
488  " to the given station for the given TID");
489  prevBar = bar;
490  continue;
491  }
492 
493  auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid);
494  if (!agreement)
495  {
496  NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
497  << " was torn down");
498  queue->Remove(bar);
499  continue;
500  }
501  // update BAR if the starting sequence number changed
502  if (auto seqNo = agreement->get().GetStartingSequence();
503  reqHdr.GetStartingSequence() != seqNo)
504  {
505  reqHdr.SetStartingSequence(seqNo);
506  Ptr<Packet> packet = Create<Packet>();
507  packet->AddHeader(reqHdr);
508  auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader());
509  queue->Replace(bar, updatedBar);
510  bar = updatedBar;
511  }
512  // bar is the BlockAckReq to send
513  selectedBar = bar;
514  selectedTid = tid;
515  break;
516  }
517  if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
518  {
519  return bar;
520  }
521  // not a BAR nor a Trigger Frame, continue
522  prevBar = bar;
523  }
524  }
525 
526  if (!selectedBar)
527  {
528  // check if we can send a BAR to a recipient to which a BAR can only be sent if data queued
529  auto baManager = m_mac->GetQosTxop(ac)->GetBaManager();
530  for (const auto& [recipient, tid] : baManager->GetSendBarIfDataQueuedList())
531  {
532  if (queue->PeekByTidAndAddress(tid, recipient))
533  {
534  selectedBar = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
535  baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
536  queue->Enqueue(selectedBar);
537  break;
538  }
539  }
540  }
541 
542  if (selectedBar && selectedBar->GetHeader().GetAddr2() != m_self)
543  {
544  // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
545  // and move to the appropriate container queue
546  NS_ASSERT(selectedBar->GetHeader().GetAddr2() == m_mac->GetAddress());
547  DequeueMpdu(selectedBar);
548  const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
549  auto addr1 =
550  GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(currAddr1);
551  selectedBar->GetHeader().SetAddr1(addr1);
552  selectedBar->GetHeader().SetAddr2(m_self);
553  queue->Enqueue(selectedBar);
554  }
555 
556  return selectedBar;
557 }
558 
559 bool
561  Time availableTime,
562  bool initialFrame)
563 {
564  NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
565 
566  // First, check if there is a BAR to be transmitted
567  if (!mpdu->GetHeader().IsBlockAckReq())
568  {
569  NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
570  return false;
571  }
572 
573  // Prepare the TX parameters. Note that the default ack manager expects the
574  // data TxVector in the m_txVector field to compute the BlockAck TxVector.
575  // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
576  // a few lines below.
577  WifiTxParameters txParams;
578  txParams.m_txVector =
580  txParams.m_protection = std::unique_ptr<WifiProtection>(new WifiNoProtection);
581  txParams.m_acknowledgment = GetAckManager()->TryAddMpdu(mpdu, txParams);
582 
584 
585  WifiBlockAck* blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
586  CalculateAcknowledgmentTime(blockAcknowledgment);
587  // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
588  txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
589 
590  Time barTxDuration = m_phy->CalculateTxDuration(mpdu->GetSize(),
591  blockAcknowledgment->blockAckTxVector,
592  m_phy->GetPhyBand());
593 
594  // if the available time is limited and we are not transmitting the initial
595  // frame of the TXOP, we have to check that this frame and its response fit
596  // within the given time limits
597  if (availableTime != Time::Min() && !initialFrame &&
598  barTxDuration + m_phy->GetSifs() + blockAcknowledgment->acknowledgmentTime > availableTime)
599  {
600  NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
601  return false;
602  }
603 
604  // we can transmit the BlockAckReq frame
605  SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
606  return true;
607 }
608 
609 bool
611  Time availableTime,
612  bool initialFrame)
613 {
614  NS_ASSERT(peekedItem && peekedItem->GetHeader().IsQosData() &&
615  !peekedItem->GetHeader().GetAddr1().IsBroadcast() && !peekedItem->IsFragment());
616  NS_LOG_FUNCTION(this << *peekedItem << availableTime << initialFrame);
617 
618  Ptr<QosTxop> edca = m_mac->GetQosTxop(peekedItem->GetHeader().GetQosTid());
619  WifiTxParameters txParams;
620  txParams.m_txVector =
621  GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
622  Ptr<WifiMpdu> mpdu =
623  edca->GetNextMpdu(m_linkId, peekedItem, txParams, availableTime, initialFrame);
624 
625  if (!mpdu)
626  {
627  NS_LOG_DEBUG("Not enough time to transmit a frame");
628  return false;
629  }
630 
631  // try A-MPDU aggregation
632  std::vector<Ptr<WifiMpdu>> mpduList =
633  m_mpduAggregator->GetNextAmpdu(mpdu, txParams, availableTime);
634  NS_ASSERT(txParams.m_acknowledgment);
635 
636  if (mpduList.size() > 1)
637  {
638  // A-MPDU aggregation succeeded
639  SendPsduWithProtection(Create<WifiPsdu>(std::move(mpduList)), txParams);
640  }
641  else if (txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
642  {
643  // a QoS data frame using the Block Ack policy can be followed by a BlockAckReq
644  // frame and a BlockAck frame. Such a sequence is handled by the HT FEM
645  SendPsduWithProtection(Create<WifiPsdu>(mpdu, false), txParams);
646  }
647  else
648  {
649  // transmission can be handled by the base FEM
650  SendMpduWithProtection(mpdu, txParams);
651  }
652 
653  return true;
654 }
655 
656 void
658 {
659  NS_LOG_FUNCTION(this << acknowledgment);
660  NS_ASSERT(acknowledgment);
661 
662  if (acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
663  {
664  WifiBlockAck* blockAcknowledgment = static_cast<WifiBlockAck*>(acknowledgment);
665  Time baTxDuration = m_phy->CalculateTxDuration(GetBlockAckSize(blockAcknowledgment->baType),
666  blockAcknowledgment->blockAckTxVector,
667  m_phy->GetPhyBand());
668  blockAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + baTxDuration;
669  }
670  else if (acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
671  {
672  WifiBarBlockAck* barBlockAcknowledgment = static_cast<WifiBarBlockAck*>(acknowledgment);
673  Time barTxDuration =
674  m_phy->CalculateTxDuration(GetBlockAckRequestSize(barBlockAcknowledgment->barType),
675  barBlockAcknowledgment->blockAckReqTxVector,
676  m_phy->GetPhyBand());
677  Time baTxDuration =
678  m_phy->CalculateTxDuration(GetBlockAckSize(barBlockAcknowledgment->baType),
679  barBlockAcknowledgment->blockAckTxVector,
680  m_phy->GetPhyBand());
681  barBlockAcknowledgment->acknowledgmentTime =
682  2 * m_phy->GetSifs() + barTxDuration + baTxDuration;
683  }
684  else
685  {
687  }
688 }
689 
690 void
692 {
693  ForwardPsduDown(GetWifiPsdu(mpdu, txVector), txVector);
694 }
695 
698 {
699  return Create<WifiPsdu>(mpdu, false);
700 }
701 
702 void
704 {
705  NS_LOG_FUNCTION(this << *mpdu);
706 
707  if (mpdu->GetHeader().IsQosData())
708  {
709  uint8_t tid = mpdu->GetHeader().GetQosTid();
710  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
711 
712  if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
713  {
714  // notify the BA manager that the MPDU was acknowledged
715  edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
716  }
717  }
718  else if (mpdu->GetHeader().IsAction())
719  {
720  auto addr1 = mpdu->GetHeader().GetAddr1();
721  auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
722  WifiActionHeader actionHdr;
723  Ptr<Packet> p = mpdu->GetPacket()->Copy();
724  p->RemoveHeader(actionHdr);
725  if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
726  {
728  {
729  MgtDelBaHeader delBa;
730  p->PeekHeader(delBa);
731  auto tid = delBa.GetTid();
732  if (delBa.IsByOriginator())
733  {
734  GetBaManager(tid)->DestroyOriginatorAgreement(address, tid);
735  }
736  else
737  {
738  GetBaManager(tid)->DestroyRecipientAgreement(address, tid);
739  }
740  }
742  {
743  // Setup ADDBA response timeout
744  MgtAddBaRequestHeader addBa;
745  p->PeekHeader(addBa);
746  Ptr<QosTxop> edca = m_mac->GetQosTxop(addBa.GetTid());
749  edca,
750  address,
751  addBa.GetTid());
752  }
754  {
755  // A recipient Block Ack agreement must exist
757  p->PeekHeader(addBa);
758  auto tid = addBa.GetTid();
759  NS_ASSERT_MSG(GetBaManager(tid)->GetAgreementAsRecipient(address, tid),
760  "Recipient BA agreement {" << address << ", " << +tid
761  << "} not found");
762  m_pendingAddBaResp.erase({address, tid});
763  }
764  }
765  }
767 }
768 
769 void
771 {
772  NS_LOG_DEBUG(this);
773 
775  {
776  // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
777  // be transmitted (as responses) the following within the current TXOP:
778  // f) Any number of BlockAckReq frames
779  // (Sec. 10.22.2.8 of 802.11-2016)
780  NS_LOG_DEBUG("Schedule a transmission from Block Ack Manager in a SIFS");
783 
784  // TXOP limit is null, hence the txopDuration parameter is unused
785  Simulator::Schedule(m_phy->GetSifs(), fp, this, m_edca, Seconds(0));
786  }
787  else
788  {
790  }
791 }
792 
793 void
795 {
796  NS_LOG_FUNCTION(this << *mpdu);
797 
798  if (mpdu->GetHeader().IsQosData())
799  {
800  GetBaManager(mpdu->GetHeader().GetQosTid())->NotifyDiscardedMpdu(mpdu);
801  }
802  else if (mpdu->GetHeader().IsAction())
803  {
804  WifiActionHeader actionHdr;
805  mpdu->GetPacket()->PeekHeader(actionHdr);
806  if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
808  {
809  uint8_t tid = GetTid(mpdu->GetPacket(), mpdu->GetHeader());
810  auto recipient = mpdu->GetHeader().GetAddr1();
811  // if the recipient is an MLD, use its MLD address
812  if (auto mldAddr = GetWifiRemoteStationManager()->GetMldAddress(recipient))
813  {
814  recipient = *mldAddr;
815  }
816  if (auto agreement = GetBaManager(tid)->GetAgreementAsOriginator(recipient, tid);
817  agreement && agreement->get().IsPending())
818  {
819  NS_LOG_DEBUG("No ACK after ADDBA request");
820  Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
821  qosTxop->NotifyOriginatorAgreementNoReply(recipient, tid);
824  qosTxop,
825  recipient,
826  tid);
827  }
828  }
829  }
831 }
832 
833 void
835 {
836  NS_LOG_FUNCTION(this << *mpdu);
837 
838  if (mpdu->GetHeader().IsQosData())
839  {
840  uint8_t tid = mpdu->GetHeader().GetQosTid();
841  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
842 
843  if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
844  {
845  // notify the BA manager that the MPDU was not acknowledged
846  edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
847  return;
848  }
849  }
851 }
852 
853 void
855 {
856  NS_LOG_FUNCTION(this << *psdu);
857 
858  auto tids = psdu->GetTids();
859 
860  if (tids.empty() || // no QoS data frames included
861  !m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin()))
862  {
864  return;
865  }
866 
867  // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
868  auto mpduIt = psdu->end();
869 
870  do
871  {
872  std::advance(mpduIt, -1);
873 
874  const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
875  if (hdr.IsQosData())
876  {
877  uint8_t tid = hdr.GetQosTid();
879 
880  if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
881  {
882  // The MPDU has never been transmitted, so we can make its sequence
883  // number available again if it is the highest sequence number
884  // assigned by the MAC TX middle
885  uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
886 
887  if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
888  {
889  (*mpduIt)->UnassignSeqNo();
890  m_txMiddle->SetSequenceNumberFor(&hdr);
891 
892  NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
893  << ", next sequence "
894  "number for dest="
895  << hdr.GetAddr1() << ",tid=" << +tid << " is "
896  << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
897  }
898  }
899  }
900  } while (mpduIt != psdu->begin());
901 }
902 
903 Time
905 {
906  NS_LOG_FUNCTION(this << txDuration << &txParams);
907 
908  NS_ASSERT(m_edca);
909 
911  {
912  NS_ASSERT(txParams.m_acknowledgment &&
913  txParams.m_acknowledgment->acknowledgmentTime != Time::Min());
914  return txParams.m_acknowledgment->acknowledgmentTime;
915  }
916 
917  // under multiple protection settings, if the TXOP limit is not null, Duration/ID
918  // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
919  // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
920  // of 802.11-2016)
921  return std::max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
922 }
923 
924 void
926 {
927  NS_LOG_FUNCTION(this << psdu << &txParams);
928 
929  m_psdu = psdu;
930  m_txParams = std::move(txParams);
931 
932 #ifdef NS3_BUILD_PROFILE_DEBUG
933  // If protection is required, the MPDUs must be stored in some queue because
934  // they are not put back in a queue if the RTS/CTS exchange fails
936  {
937  for (const auto& mpdu : *PeekPointer(m_psdu))
938  {
939  NS_ASSERT(mpdu->GetHeader().IsCtl() || mpdu->IsQueued());
940  }
941  }
942 #endif
943 
944  // Make sure that the acknowledgment time has been computed, so that SendRts()
945  // and SendCtsToSelf() can reuse this value.
947 
948  if (m_txParams.m_acknowledgment->acknowledgmentTime == Time::Min())
949  {
951  }
952 
953  // Set QoS Ack policy
955 
956  for (const auto& mpdu : *PeekPointer(m_psdu))
957  {
958  if (mpdu->IsQueued())
959  {
960  mpdu->SetInFlight(m_linkId);
961  }
962  }
963 
965  {
967  }
969  {
971  }
972  else if (m_txParams.m_protection->method == WifiProtection::NONE)
973  {
974  SendPsdu();
975  }
976  else
977  {
978  NS_ABORT_MSG("Unknown protection type");
979  }
980 }
981 
982 void
984 {
985  NS_LOG_FUNCTION(this << *rts << txVector);
986 
987  if (!m_psdu)
988  {
989  // A CTS Timeout occurred when protecting a single MPDU is handled by the
990  // parent classes
992  return;
993  }
994 
996  m_psdu = nullptr;
997 }
998 
999 void
1001 {
1002  NS_LOG_FUNCTION(this);
1003 
1004  Time txDuration =
1006 
1008 
1010  {
1012 
1013  std::set<uint8_t> tids = m_psdu->GetTids();
1014  NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
1015 
1016  if (tids.empty() || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
1017  {
1018  // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
1020  }
1021  }
1023  {
1025 
1026  // the timeout duration is "aSIFSTime + aSlotTime + aRxPHYStartDelay, starting
1027  // at the PHY-TXEND.confirm primitive" (section 10.3.2.9 or 10.22.2.2 of 802.11-2016).
1028  // aRxPHYStartDelay equals the time to transmit the PHY header.
1029  WifiBlockAck* blockAcknowledgment =
1030  static_cast<WifiBlockAck*>(m_txParams.m_acknowledgment.get());
1031 
1032  Time timeout =
1033  txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
1037  timeout,
1039  this,
1040  m_psdu,
1043  }
1045  {
1047 
1048  // schedule the transmission of a BAR in a SIFS
1049  std::set<uint8_t> tids = m_psdu->GetTids();
1050  NS_ABORT_MSG_IF(tids.size() > 1,
1051  "Acknowledgment method incompatible with a Multi-TID A-MPDU");
1052  uint8_t tid = *tids.begin();
1053 
1054  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1055  GetBaManager(tid)->ScheduleBar(edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid));
1056 
1058  }
1059  else
1060  {
1061  NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
1062  << m_txParams.m_acknowledgment.get() << ")");
1063  }
1064 
1065  // transmit the PSDU
1066  if (m_psdu->GetNMpdus() > 1)
1067  {
1069  }
1070  else
1071  {
1073  }
1074 
1076  {
1077  // we are done in case the A-MPDU does not require acknowledgment
1078  m_psdu = nullptr;
1079  }
1080 }
1081 
1082 void
1084 {
1085  NS_LOG_FUNCTION(this << psdu);
1086 
1087  // use an array to avoid computing the queue size for every MPDU in the PSDU
1088  std::array<std::optional<uint8_t>, 8> queueSizeForTid;
1089 
1090  for (const auto& mpdu : *PeekPointer(psdu))
1091  {
1092  WifiMacHeader& hdr = mpdu->GetHeader();
1093 
1094  if (hdr.IsQosData())
1095  {
1096  uint8_t tid = hdr.GetQosTid();
1097  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1098 
1099  if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
1100  {
1101  // set the Queue Size subfield of the QoS Control field
1102  if (!queueSizeForTid[tid].has_value())
1103  {
1104  queueSizeForTid[tid] = edca->GetQosQueueSize(tid, hdr.GetAddr1());
1105  }
1106 
1107  hdr.SetQosEosp();
1108  hdr.SetQosQueueSize(queueSizeForTid[tid].value());
1109  }
1110 
1111  if (hdr.HasData())
1112  {
1113  edca->CompleteMpduTx(mpdu);
1114  }
1115  }
1116  }
1117 }
1118 
1119 void
1121 {
1122  NS_LOG_DEBUG(this << psdu);
1123 
1124  for (const auto& mpdu : *PeekPointer(psdu))
1125  {
1126  DequeueMpdu(mpdu);
1127  }
1128 }
1129 
1130 void
1132 {
1133  NS_LOG_FUNCTION(this << psdu << txVector);
1134 
1135  NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
1136  NotifyTxToEdca(psdu);
1137 
1138  if (psdu->IsAggregate())
1139  {
1140  txVector.SetAggregation(true);
1141  }
1142 
1143  m_phy->Send(psdu, txVector);
1144 }
1145 
1146 bool
1148  const WifiTxParameters& txParams,
1149  Time ppduDurationLimit) const
1150 {
1151  NS_ASSERT(mpdu);
1152  NS_LOG_FUNCTION(this << *mpdu << &txParams << ppduDurationLimit);
1153 
1154  Mac48Address receiver = mpdu->GetHeader().GetAddr1();
1155  uint32_t ampduSize = txParams.GetSizeIfAddMpdu(mpdu);
1156 
1157  if (txParams.GetSize(receiver) > 0)
1158  {
1159  // we are attempting to perform A-MPDU aggregation, hence we have to check
1160  // that we meet the limit on the max A-MPDU size
1161  uint8_t tid;
1162  const WifiTxParameters::PsduInfo* info;
1163 
1164  if (mpdu->GetHeader().IsQosData())
1165  {
1166  tid = mpdu->GetHeader().GetQosTid();
1167  }
1168  else if ((info = txParams.GetPsduInfo(receiver)) && !info->seqNumbers.empty())
1169  {
1170  tid = info->seqNumbers.begin()->first;
1171  }
1172  else
1173  {
1174  NS_ABORT_MSG("Cannot aggregate a non-QoS data frame to an A-MPDU that does"
1175  " not contain any QoS data frame");
1176  }
1177 
1178  WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1179 
1180  if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1181  {
1182  return false;
1183  }
1184  }
1185 
1186  return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1187 }
1188 
1189 bool
1191  Mac48Address receiver,
1192  uint8_t tid,
1193  WifiModulationClass modulation) const
1194 {
1195  NS_LOG_FUNCTION(this << ampduSize << receiver << +tid << modulation);
1196 
1197  uint32_t maxAmpduSize = m_mpduAggregator->GetMaxAmpduSize(receiver, tid, modulation);
1198 
1199  if (maxAmpduSize == 0)
1200  {
1201  NS_LOG_DEBUG("A-MPDU aggregation disabled");
1202  return false;
1203  }
1204 
1205  if (ampduSize > maxAmpduSize)
1206  {
1207  NS_LOG_DEBUG("the frame does not meet the constraint on max A-MPDU size (" << maxAmpduSize
1208  << ")");
1209  return false;
1210  }
1211  return true;
1212 }
1213 
1214 bool
1216  WifiTxParameters& txParams,
1217  Time availableTime) const
1218 {
1219  NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1220  NS_LOG_FUNCTION(this << *msdu << &txParams << availableTime);
1221 
1222  // check if aggregating the given MSDU requires a different protection method
1223  NS_ASSERT(txParams.m_protection);
1224  Time protectionTime = txParams.m_protection->protectionTime;
1225 
1226  std::unique_ptr<WifiProtection> protection;
1227  protection = GetProtectionManager()->TryAggregateMsdu(msdu, txParams);
1228  bool protectionSwapped = false;
1229 
1230  if (protection)
1231  {
1232  // the protection method has changed, calculate the new protection time
1233  CalculateProtectionTime(protection.get());
1234  protectionTime = protection->protectionTime;
1235  // swap unique pointers, so that the txParams that is passed to the next
1236  // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1237  txParams.m_protection.swap(protection);
1238  protectionSwapped = true;
1239  }
1240  NS_ASSERT(protectionTime != Time::Min());
1241 
1242  // check if aggregating the given MSDU requires a different acknowledgment method
1243  NS_ASSERT(txParams.m_acknowledgment);
1244  Time acknowledgmentTime = txParams.m_acknowledgment->acknowledgmentTime;
1245 
1246  std::unique_ptr<WifiAcknowledgment> acknowledgment;
1247  acknowledgment = GetAckManager()->TryAggregateMsdu(msdu, txParams);
1248  bool acknowledgmentSwapped = false;
1249 
1250  if (acknowledgment)
1251  {
1252  // the acknowledgment method has changed, calculate the new acknowledgment time
1253  CalculateAcknowledgmentTime(acknowledgment.get());
1254  acknowledgmentTime = acknowledgment->acknowledgmentTime;
1255  // swap unique pointers, so that the txParams that is passed to the next
1256  // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1257  txParams.m_acknowledgment.swap(acknowledgment);
1258  acknowledgmentSwapped = true;
1259  }
1260  NS_ASSERT(acknowledgmentTime != Time::Min());
1261 
1262  Time ppduDurationLimit = Time::Min();
1263  if (availableTime != Time::Min())
1264  {
1265  ppduDurationLimit = availableTime - protectionTime - acknowledgmentTime;
1266  }
1267 
1268  if (!IsWithinLimitsIfAggregateMsdu(msdu, txParams, ppduDurationLimit))
1269  {
1270  // adding MPDU failed, restore protection and acknowledgment methods
1271  // if they were swapped
1272  if (protectionSwapped)
1273  {
1274  txParams.m_protection.swap(protection);
1275  }
1276  if (acknowledgmentSwapped)
1277  {
1278  txParams.m_acknowledgment.swap(acknowledgment);
1279  }
1280  return false;
1281  }
1282 
1283  // the given MPDU can be added, hence update the txParams
1284  txParams.AggregateMsdu(msdu);
1285  UpdateTxDuration(msdu->GetHeader().GetAddr1(), txParams);
1286 
1287  return true;
1288 }
1289 
1290 bool
1292  const WifiTxParameters& txParams,
1293  Time ppduDurationLimit) const
1294 {
1295  NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1296  NS_LOG_FUNCTION(this << *msdu << &txParams << ppduDurationLimit);
1297 
1298  std::pair<uint16_t, uint32_t> ret = txParams.GetSizeIfAggregateMsdu(msdu);
1299  Mac48Address receiver = msdu->GetHeader().GetAddr1();
1300  uint8_t tid = msdu->GetHeader().GetQosTid();
1301  WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1302 
1303  // Check that the limit on A-MSDU size is met
1304  uint16_t maxAmsduSize = m_msduAggregator->GetMaxAmsduSize(receiver, tid, modulation);
1305 
1306  if (maxAmsduSize == 0)
1307  {
1308  NS_LOG_DEBUG("A-MSDU aggregation disabled");
1309  return false;
1310  }
1311 
1312  if (ret.first > maxAmsduSize)
1313  {
1314  NS_LOG_DEBUG("No other MSDU can be aggregated: maximum A-MSDU size (" << maxAmsduSize
1315  << ") reached ");
1316  return false;
1317  }
1318 
1319  const WifiTxParameters::PsduInfo* info = txParams.GetPsduInfo(msdu->GetHeader().GetAddr1());
1320  NS_ASSERT(info);
1321 
1322  if (info->ampduSize > 0)
1323  {
1324  // the A-MSDU being built is aggregated to other MPDUs in an A-MPDU.
1325  // Check that the limit on A-MPDU size is met.
1326  if (!IsWithinAmpduSizeLimit(ret.second, receiver, tid, modulation))
1327  {
1328  return false;
1329  }
1330  }
1331 
1332  return IsWithinSizeAndTimeLimits(ret.second, receiver, txParams, ppduDurationLimit);
1333 }
1334 
1335 void
1337 {
1338  NS_LOG_FUNCTION(this << *psdu << txVector);
1339 
1341 
1342  bool resetCw;
1343  MissedBlockAck(psdu, txVector, resetCw);
1344 
1345  NS_ASSERT(m_edca);
1346 
1347  if (resetCw)
1348  {
1350  }
1351  else
1352  {
1354  }
1355 
1356  m_psdu = nullptr;
1358 }
1359 
1360 void
1362  const WifiTxVector& txVector,
1363  bool& resetCw)
1364 {
1365  NS_LOG_FUNCTION(this << psdu << txVector << resetCw);
1366 
1367  auto recipient = psdu->GetAddr1();
1368  auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
1369  bool isBar;
1370  uint8_t tid;
1371 
1372  if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
1373  {
1374  isBar = true;
1375  CtrlBAckRequestHeader baReqHdr;
1376  psdu->GetPayload(0)->PeekHeader(baReqHdr);
1377  tid = baReqHdr.GetTidInfo();
1378  }
1379  else
1380  {
1381  isBar = false;
1383  ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
1384  std::set<uint8_t> tids = psdu->GetTids();
1385  NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
1386  NS_ASSERT(!tids.empty());
1387  tid = *tids.begin();
1388  }
1389 
1390  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1391 
1392  if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
1393  {
1394  // we have to send a BlockAckReq, if needed
1395  if (GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld))
1396  {
1397  NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
1408  if (isBar)
1409  {
1410  psdu->GetHeader(0).SetRetry();
1411  }
1412  else
1413  {
1414  // missed block ack after data frame with Implicit BAR Ack policy
1415  GetBaManager(tid)->ScheduleBar(edca->PrepareBlockAckRequest(recipient, tid));
1416  }
1417  resetCw = false;
1418  }
1419  else
1420  {
1421  NS_LOG_DEBUG("Missed Block Ack, do not transmit a BlockAckReq");
1422  // if a BA agreement exists, we can get here if there is no outstanding
1423  // MPDU whose lifetime has not expired yet.
1425  if (isBar)
1426  {
1427  DequeuePsdu(psdu);
1428  }
1429  if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
1430  {
1431  // schedule a BlockAckRequest to be sent only if there are data frames queued
1432  // for this recipient
1433  GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
1434  }
1435  resetCw = true;
1436  }
1437  }
1438  else
1439  {
1440  // we have to retransmit the data frames, if needed
1441  if (!GetWifiRemoteStationManager()->NeedRetransmission(*psdu->begin()))
1442  {
1443  NS_LOG_DEBUG("Missed Block Ack, do not retransmit the data frames");
1445  for (const auto& mpdu : *PeekPointer(psdu))
1446  {
1447  NotifyPacketDiscarded(mpdu);
1448  DequeueMpdu(mpdu);
1449  }
1450  resetCw = true;
1451  }
1452  else
1453  {
1454  NS_LOG_DEBUG("Missed Block Ack, retransmit data frames");
1455  GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
1456  resetCw = false;
1457  }
1458  }
1459 }
1460 
1461 void
1463  Time durationId,
1464  WifiTxVector& blockAckTxVector,
1465  double rxSnr)
1466 {
1467  NS_LOG_FUNCTION(this << durationId << blockAckTxVector << rxSnr);
1468 
1469  WifiMacHeader hdr;
1471  auto addr1 = agreement.GetPeer();
1472  if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
1473  {
1474  addr1 = *originator;
1475  }
1476  hdr.SetAddr1(addr1);
1477  hdr.SetAddr2(m_self);
1478  hdr.SetDsNotFrom();
1479  hdr.SetDsNotTo();
1480 
1481  CtrlBAckResponseHeader blockAck;
1482  blockAck.SetType(agreement.GetBlockAckType());
1483  blockAck.SetTidInfo(agreement.GetTid());
1484  agreement.FillBlockAckBitmap(&blockAck);
1485 
1486  Ptr<Packet> packet = Create<Packet>();
1487  packet->AddHeader(blockAck);
1488  Ptr<WifiPsdu> psdu = GetWifiPsdu(Create<WifiMpdu>(packet, hdr), blockAckTxVector);
1489 
1490  // 802.11-2016, Section 9.2.5.7: In a BlockAck frame transmitted in response
1491  // to a BlockAckReq frame or transmitted in response to a frame containing an
1492  // implicit block ack request, the Duration/ID field is set to the value obtained
1493  // from the Duration/ ID field of the frame that elicited the response minus the
1494  // time, in microseconds between the end of the PPDU carrying the frame that
1495  // elicited the response and the end of the PPDU carrying the BlockAck frame.
1496  Time baDurationId = durationId - m_phy->GetSifs() -
1497  m_phy->CalculateTxDuration(psdu, blockAckTxVector, m_phy->GetPhyBand());
1498  // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016)
1499  if (baDurationId.IsStrictlyNegative())
1500  {
1501  baDurationId = Seconds(0);
1502  }
1503  psdu->GetHeader(0).SetDuration(baDurationId);
1504 
1505  SnrTag tag;
1506  tag.Set(rxSnr);
1507  psdu->GetPayload(0)->AddPacketTag(tag);
1508 
1509  ForwardPsduDown(psdu, blockAckTxVector);
1510 }
1511 
1512 void
1514  RxSignalInfo rxSignalInfo,
1515  const WifiTxVector& txVector,
1516  bool inAmpdu)
1517 {
1518  // The received MPDU is either broadcast or addressed to this station
1519  NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1520 
1521  double rxSnr = rxSignalInfo.snr;
1522  const WifiMacHeader& hdr = mpdu->GetHeader();
1523 
1524  if (hdr.IsCtl())
1525  {
1526  if (hdr.IsCts() && m_txTimer.IsRunning() &&
1528  {
1529  NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1530  NS_ASSERT(hdr.GetAddr1() == m_self);
1531 
1532  Mac48Address sender = m_psdu->GetAddr1();
1533  NS_LOG_DEBUG("Received CTS from=" << sender);
1534 
1535  SnrTag tag;
1536  mpdu->GetPacket()->PeekPacketTag(tag);
1537  GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1539  rxSnr,
1540  txVector.GetMode(),
1541  tag.Get());
1542 
1543  m_txTimer.Cancel();
1546  }
1547  else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1549  {
1550  Mac48Address sender = hdr.GetAddr2();
1551  NS_LOG_DEBUG("Received BlockAck from=" << sender);
1552 
1553  SnrTag tag;
1554  mpdu->GetPacket()->PeekPacketTag(tag);
1555 
1556  // notify the Block Ack Manager
1557  CtrlBAckResponseHeader blockAck;
1558  mpdu->GetPacket()->PeekHeader(blockAck);
1559  uint8_t tid = blockAck.GetTidInfo();
1560  std::pair<uint16_t, uint16_t> ret =
1561  GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
1562  blockAck,
1563  m_mac->GetMldAddress(sender).value_or(sender),
1564  {tid});
1566  ret.first,
1567  ret.second,
1568  rxSnr,
1569  tag.Get(),
1571 
1572  // cancel the timer
1573  m_txTimer.Cancel();
1575 
1576  // Reset the CW
1578 
1579  // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
1580  if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
1581  {
1583  }
1584  m_psdu = nullptr;
1586  }
1587  else if (hdr.IsBlockAckReq())
1588  {
1589  NS_ASSERT(hdr.GetAddr1() == m_self);
1590  NS_ABORT_MSG_IF(inAmpdu, "BlockAckReq in A-MPDU is not supported");
1591 
1592  Mac48Address sender = hdr.GetAddr2();
1593  NS_LOG_DEBUG("Received BlockAckReq from=" << sender);
1594 
1595  CtrlBAckRequestHeader blockAckReq;
1596  mpdu->GetPacket()->PeekHeader(blockAckReq);
1597  NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1598  uint8_t tid = blockAckReq.GetTidInfo();
1599 
1600  auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(sender, tid);
1601 
1602  if (!agreement)
1603  {
1604  NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1605  return;
1606  }
1607 
1608  GetBaManager(tid)->NotifyGotBlockAckRequest(
1609  m_mac->GetMldAddress(sender).value_or(sender),
1610  tid,
1611  blockAckReq.GetStartingSequence());
1612 
1613  NS_LOG_DEBUG("Schedule Block Ack");
1615  m_phy->GetSifs(),
1617  this,
1618  *agreement,
1619  hdr.GetDuration(),
1620  GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
1621  rxSnr);
1622  }
1623  else
1624  {
1625  // the received control frame cannot be handled here
1626  QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1627  }
1628  return;
1629  }
1630 
1631  if (hdr.IsQosData() && hdr.HasData() && hdr.GetAddr1() == m_self)
1632  {
1633  uint8_t tid = hdr.GetQosTid();
1634 
1636  {
1637  // a Block Ack agreement has been established
1638  NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
1639 
1640  GetBaManager(tid)->NotifyGotMpdu(mpdu);
1641 
1642  if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
1643  {
1644  NS_LOG_DEBUG("Schedule Normal Ack");
1647  this,
1648  hdr,
1649  txVector,
1650  rxSnr);
1651  }
1652  return;
1653  }
1654  // We let the QosFrameExchangeManager handle QoS data frame not belonging
1655  // to a Block Ack agreement
1656  }
1657 
1658  QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1659 }
1660 
1661 void
1663  const RxSignalInfo& rxSignalInfo,
1664  const WifiTxVector& txVector,
1665  const std::vector<bool>& perMpduStatus)
1666 {
1667  std::set<uint8_t> tids = psdu->GetTids();
1668 
1669  // Multi-TID A-MPDUs are not supported yet
1670  if (tids.size() == 1)
1671  {
1672  uint8_t tid = *tids.begin();
1673  WifiMacHeader::QosAckPolicy ackPolicy = psdu->GetAckPolicyForTid(tid);
1674  NS_ASSERT(psdu->GetNMpdus() > 1);
1675 
1676  if (ackPolicy == WifiMacHeader::NORMAL_ACK)
1677  {
1678  // Normal Ack or Implicit Block Ack Request
1679  NS_LOG_DEBUG("Schedule Block Ack");
1680  auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
1681  NS_ASSERT(agreement);
1682 
1684  m_phy->GetSifs(),
1686  this,
1687  *agreement,
1688  psdu->GetDuration(),
1689  GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
1690  rxSignalInfo.snr);
1691  }
1692  }
1693 }
1694 
1695 } // namespace ns3
#define max(a, b)
Definition: 80211b.c:43
BlockAckType GetBlockAckType() const
Get the type of the Block Acks sent by the recipient of this agreement.
uint8_t GetTid() const
Return the Traffic ID (TID).
Mac48Address GetPeer() const
Return the peer address.
void NotifyAckTimeoutResetNow()
Notify that ack timer has reset.
void NotifyAckTimeoutStartNow(Time duration)
Notify that ack timer has started for the given duration.
void NotifyCtsTimeoutResetNow()
Notify that CTS timer has reset.
Headers for BlockAckRequest.
Definition: ctrl-headers.h:52
uint16_t GetStartingSequence() const
Return the starting sequence number.
uint8_t GetTidInfo() const
Return the Traffic ID (TID).
bool IsMultiTid() const
Check if the current Ack Policy has Multi-TID Block Ack.
void SetStartingSequence(uint16_t seq)
Set the starting sequence number from the given raw sequence control field.
Headers for BlockAck response.
Definition: ctrl-headers.h:203
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...
void SetTidInfo(uint8_t tid, std::size_t index=0)
For Block Ack variants other than Multi-STA Block Ack, set the TID_INFO subfield of the BA Control fi...
void SetType(BlockAckType type)
Set the block ack type.
void DoCtsTimeout(Ptr< WifiPsdu > psdu)
Take required actions when the CTS timer fired after sending an RTS to protect the given PSDU expires...
uint8_t m_linkId
the ID of the link this object is associated with
Ptr< WifiMac > m_mac
the MAC layer on this station
virtual void SetWifiMac(const Ptr< WifiMac > mac)
Set the MAC layer to use.
void SendMpduWithProtection(Ptr< WifiMpdu > mpdu, WifiTxParameters &txParams)
Send an MPDU with the given TX parameters (with the specified protection).
Ptr< WifiRemoteStationManager > GetWifiRemoteStationManager() const
void UpdateTxDuration(Mac48Address receiver, WifiTxParameters &txParams) const
Update the TX duration field of the given TX parameters after that the PSDU addressed to the given re...
virtual void CalculateAcknowledgmentTime(WifiAcknowledgment *acknowledgment) const
Calculate the time required to acknowledge a frame according to the given acknowledgment method.
Ptr< MacTxMiddle > m_txMiddle
the MAC TX Middle on this station
void SendNormalAck(const WifiMacHeader &hdr, const WifiTxVector &dataTxVector, double dataSnr)
Send Normal Ack.
Mac48Address m_self
the MAC address of this device
uint16_t m_allowedWidth
the allowed width in MHz for the current transmission
virtual void NotifyPacketDiscarded(Ptr< const WifiMpdu > mpdu)
Pass the given MPDU, discarded because of the max retry limit was reached, to the MPDU dropped callba...
WifiTxTimer m_txTimer
the timer set upon frame transmission
virtual void RetransmitMpduAfterMissedAck(Ptr< WifiMpdu > mpdu) const
Retransmit an MPDU that was not acknowledged.
void SendRts(const WifiTxParameters &txParams)
Send RTS to begin RTS-CTS-Data-Ack transaction.
virtual void NotifyReceivedNormalAck(Ptr< WifiMpdu > mpdu)
Notify other components that an MPDU was acknowledged.
void SendCtsToSelf(const WifiTxParameters &txParams)
Send CTS for a CTS-to-self mechanism.
virtual void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector)
Called when the CTS timeout expires.
virtual void CalculateProtectionTime(WifiProtection *protection) const
Calculate the time required to protect a frame according to the given protection method.
Ptr< WifiAckManager > GetAckManager() const
Get the Acknowledgment Manager used by this node.
virtual void DequeueMpdu(Ptr< const WifiMpdu > mpdu)
Dequeue the given MPDU from the queue in which it is stored.
Ptr< WifiProtectionManager > GetProtectionManager() const
Get the Protection Manager used by this node.
Ptr< MacRxMiddle > m_rxMiddle
the MAC RX Middle on this station
Ptr< WifiPhy > m_phy
the PHY layer on this station
virtual void ReleaseSequenceNumbers(Ptr< const WifiPsdu > psdu) const
Make the sequence numbers of MPDUs included in the given PSDU available again if the MPDUs have never...
Mac48Address m_bssid
BSSID address (Mac48Address)
Ptr< ChannelAccessManager > m_channelAccessManager
the channel access manager
HtFrameExchangeManager handles the frame exchange sequences for HT stations.
Ptr< MpduAggregator > m_mpduAggregator
A-MPDU aggregator.
void ReceiveMpdu(Ptr< const WifiMpdu > mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, bool inAmpdu) override
This method handles the reception of an MPDU (possibly included in an A-MPDU)
void SendAddBaRequest(Mac48Address recipient, uint8_t tid, uint16_t startingSeq, uint16_t timeout, bool immediateBAck)
Sends an ADDBA Request to establish a block ack agreement with STA addressed by recipient for TID tid...
void SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator)
Sends DELBA frame to cancel a block ack agreement with STA addressed by addr for TID tid.
std::map< AgreementKey, Ptr< WifiMpdu > > m_pendingAddBaResp
pending ADDBA_RESPONSE frames indexed by agreement key
void SendAddBaResponse(const MgtAddBaRequestHeader *reqHdr, Mac48Address originator)
This method can be called to accept a received ADDBA Request.
void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector) override
Called when the CTS timeout expires.
Ptr< WifiPsdu > m_psdu
the A-MPDU being transmitted
Ptr< BlockAckManager > GetBaManager(uint8_t tid) const
Get the Block Ack Manager handling the given TID.
virtual Ptr< WifiPsdu > GetWifiPsdu(Ptr< WifiMpdu > mpdu, const WifiTxVector &txVector) const
Get a PSDU containing the given MPDU.
virtual void BlockAckTimeout(Ptr< WifiPsdu > psdu, const WifiTxVector &txVector)
Called when the BlockAck timeout expires.
Ptr< WifiMpdu > GetBar(AcIndex ac, std::optional< uint8_t > optTid=std::nullopt, std::optional< Mac48Address > optAddress=std::nullopt)
Get the next BlockAckRequest or MU-BAR Trigger Frame to send, if any.
virtual Time GetPsduDurationId(Time txDuration, const WifiTxParameters &txParams) const
Compute how to set the Duration/ID field of PSDUs that do not include fragments.
virtual bool NeedSetupBlockAck(Mac48Address recipient, uint8_t tid)
A Block Ack agreement needs to be established with the given recipient for the given TID if it does n...
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
virtual bool SendMpduFromBaManager(Ptr< WifiMpdu > mpdu, Time availableTime, bool initialFrame)
If the given MPDU contains a BlockAckReq frame (the duration of which plus the response fits within t...
Ptr< MpduAggregator > GetMpduAggregator() const
Returns the aggregator used to construct A-MPDU subframes.
virtual bool IsWithinLimitsIfAggregateMsdu(Ptr< const WifiMpdu > msdu, const WifiTxParameters &txParams, Time ppduDurationLimit) const
Check if the PSDU obtained by aggregating the given MSDU to the PSDU specified by the given TX parame...
virtual bool IsWithinAmpduSizeLimit(uint32_t ampduSize, Mac48Address receiver, uint8_t tid, WifiModulationClass modulation) const
Check whether an A-MPDU of the given size meets the constraint on the maximum size for A-MPDUs sent t...
void SetWifiMac(const Ptr< WifiMac > mac) override
Set the MAC layer to use.
void ForwardMpduDown(Ptr< WifiMpdu > mpdu, WifiTxVector &txVector) override
Forward an MPDU down to the PHY layer.
Ptr< MsduAggregator > GetMsduAggregator() const
Returns the aggregator used to construct A-MSDU subframes.
void SendPsduWithProtection(Ptr< WifiPsdu > psdu, WifiTxParameters &txParams)
Send a PSDU (A-MPDU or BlockAckReq frame) requesting a BlockAck frame or a BlockAckReq frame followed...
void NotifyReceivedNormalAck(Ptr< WifiMpdu > mpdu) override
Notify other components that an MPDU was acknowledged.
void EndReceiveAmpdu(Ptr< const WifiPsdu > psdu, const RxSignalInfo &rxSignalInfo, const WifiTxVector &txVector, const std::vector< bool > &perMpduStatus) override
This method is called when the reception of an A-MPDU including multiple MPDUs is completed.
void RetransmitMpduAfterMissedAck(Ptr< WifiMpdu > mpdu) const override
Retransmit an MPDU that was not acknowledged.
void DoDispose() override
Destructor implementation.
static TypeId GetTypeId()
Get the type ID.
bool StartFrameExchange(Ptr< QosTxop > edca, Time availableTime, bool initialFrame) override
Start a frame exchange (including protection frames and acknowledgment frames as needed) that fits wi...
WifiTxParameters m_txParams
the TX parameters for the current frame
bool IsWithinLimitsIfAddMpdu(Ptr< const WifiMpdu > mpdu, const WifiTxParameters &txParams, Time ppduDurationLimit) const override
Check if the PSDU obtained by aggregating the given MPDU to the PSDU specified by the given TX parame...
void SendPsdu()
Send the current PSDU, which can be acknowledged by a BlockAck frame or followed by a BlockAckReq fra...
virtual uint16_t GetSupportedBaBufferSize() const
Get the maximum supported buffer size for a Block Ack agreement.
virtual bool TryAggregateMsdu(Ptr< const WifiMpdu > msdu, WifiTxParameters &txParams, Time availableTime) const
Check if aggregating an MSDU to the current MPDU (as specified by the given TX parameters) does not v...
virtual void NotifyTxToEdca(Ptr< const WifiPsdu > psdu) const
Notify the transmission of the given PSDU to the EDCAF associated with the AC the PSDU belongs to.
virtual bool SendDataFrame(Ptr< WifiMpdu > peekedItem, Time availableTime, bool initialFrame)
Given a non-broadcast QoS data frame, prepare the PSDU to transmit by attempting A-MSDU and A-MPDU ag...
void NotifyPacketDiscarded(Ptr< const WifiMpdu > mpdu) override
Pass the given MPDU, discarded because of the max retry limit was reached, to the MPDU dropped callba...
virtual void ForwardPsduDown(Ptr< const WifiPsdu > psdu, WifiTxVector &txVector)
Forward a PSDU down to the PHY layer.
void CalculateAcknowledgmentTime(WifiAcknowledgment *acknowledgment) const override
Calculate the time required to acknowledge a frame according to the given acknowledgment method.
void DequeuePsdu(Ptr< const WifiPsdu > psdu)
Dequeue the MPDUs of the given PSDU from the queue in which they are stored.
void ReleaseSequenceNumbers(Ptr< const WifiPsdu > psdu) const override
Make the sequence numbers of MPDUs included in the given PSDU available again if the MPDUs have never...
virtual void MissedBlockAck(Ptr< WifiPsdu > psdu, const WifiTxVector &txVector, bool &resetCw)
Take necessary actions when a BlockAck is missed, such as scheduling a BlockAckReq frame or the retra...
Ptr< MsduAggregator > m_msduAggregator
A-MSDU aggregator.
std::pair< Mac48Address, uint8_t > AgreementKey
agreement key typedef (MAC address and TID)
void SendBlockAck(const RecipientBlockAckAgreement &agreement, Time durationId, WifiTxVector &blockAckTxVector, double rxSnr)
Create a BlockAck frame with header equal to blockAck and start its transmission.
an EUI-48 address
Definition: mac48-address.h:46
bool IsGroup() const
Implement the header for management frames of type Add Block Ack request.
Definition: mgt-headers.h:1511
void SetBufferSize(uint16_t size)
Set buffer size.
void SetDelayedBlockAck()
Enable delayed BlockAck.
void SetAmsduSupport(bool supported)
Enable or disable A-MSDU support.
void SetImmediateBlockAck()
Enable immediate BlockAck.
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.
void SetTimeout(uint16_t timeout)
Set timeout.
void SetTid(uint8_t tid)
Set Traffic ID (TID).
void SetStartingSequence(uint16_t seq)
Set the starting sequence number.
Implement the header for management frames of type Add Block Ack response.
Definition: mgt-headers.h:1642
void SetTid(uint8_t tid)
Set Traffic ID (TID).
void SetTimeout(uint16_t timeout)
Set timeout.
void SetBufferSize(uint16_t size)
Set buffer size.
void SetStatusCode(StatusCode code)
Set the status code.
uint8_t GetTid() const
Return the Traffic ID (TID).
void SetAmsduSupport(bool supported)
Enable or disable A-MSDU support.
uint16_t GetTimeout() const
Return the timeout.
void SetDelayedBlockAck()
Enable delayed BlockAck.
void SetImmediateBlockAck()
Enable immediate BlockAck.
Implement the header for management frames of type Delete Block Ack.
Definition: mgt-headers.h:1761
void SetTid(uint8_t tid)
Set Traffic ID (TID).
void SetByRecipient()
Un-set the initiator bit in the DELBA.
uint8_t GetTid() const
Return the Traffic ID (TID).
bool IsByOriginator() const
Check if the initiator bit in the DELBA is set.
void SetByOriginator()
Set the initiator bit in the DELBA.
uint32_t RemoveHeader(Header &header)
Deserialize and remove the header from the internal buffer.
Definition: packet.cc:294
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:268
void AddPacketTag(const Tag &tag) const
Add a packet tag.
Definition: packet.cc:979
uint32_t PeekHeader(Header &header) const
Deserialize but does not remove the header from the internal buffer.
Definition: packet.cc:305
QosFrameExchangeManager handles the frame exchange sequences for QoS stations.
void ReceiveMpdu(Ptr< const WifiMpdu > mpdu, RxSignalInfo rxSignalInfo, const WifiTxVector &txVector, bool inAmpdu) override
This method handles the reception of an MPDU (possibly included in an A-MPDU)
void TransmissionFailed() override
Take necessary actions upon a transmission failure.
virtual bool StartFrameExchange(Ptr< QosTxop > edca, Time availableTime, bool initialFrame)
Start a frame exchange (including protection frames and acknowledgment frames as needed) that fits wi...
Ptr< QosTxop > m_edca
the EDCAF that gained channel access
bool StartTransmission(Ptr< Txop > edca, uint16_t allowedWidth) override
Request the FrameExchangeManager to start a frame exchange sequence.
virtual Ptr< WifiMpdu > CreateAliasIfNeeded(Ptr< WifiMpdu > mpdu) const
Create an alias of the given MPDU for transmission by this Frame Exchange Manager.
void TransmissionSucceeded() override
Take necessary actions upon a transmission success.
bool m_setQosQueueSize
whether to set the Queue Size subfield of the QoS Control field of QoS data frames
virtual bool IsWithinSizeAndTimeLimits(uint32_t ppduPayloadSize, Mac48Address receiver, const WifiTxParameters &txParams, Time ppduDurationLimit) const
Check whether the transmission time of the frame being built (as described by the given TX parameters...
void DoDispose() override
Destructor implementation.
Ptr< BlockAckManager > GetBaManager()
Get the Block Ack Manager associated with this QosTxop.
Definition: qos-txop.cc:261
Ptr< WifiMpdu > PeekNextMpdu(uint8_t linkId, uint8_t tid=8, Mac48Address recipient=Mac48Address::GetBroadcast(), Ptr< const WifiMpdu > mpdu=nullptr)
Peek the next frame to transmit on the given link to the given receiver and of the given TID from the...
Definition: qos-txop.cc:359
bool UseExplicitBarAfterMissedBlockAck() const
Return true if an explicit BlockAckRequest is sent after a missed BlockAck.
Definition: qos-txop.cc:304
Time GetAddBaResponseTimeout() const
Get the timeout for ADDBA response.
Definition: qos-txop.cc:741
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition: qos-txop.cc:766
void AddBaResponseTimeout(Mac48Address recipient, uint8_t tid)
Callback when ADDBA response is not received after timeout.
Definition: qos-txop.cc:706
uint8_t GetBlockAckThreshold() const
Return the current threshold for block ack mechanism.
Definition: qos-txop.cc:693
uint16_t GetBlockAckInactivityTimeout() const
Get the BlockAck inactivity timeout.
Definition: qos-txop.cc:700
virtual Time GetRemainingTxop(uint8_t linkId) const
Return the remaining duration in the current TXOP on the given link.
Definition: qos-txop.cc:591
void NotifyOriginatorAgreementNoReply(const Mac48Address &recipient, uint8_t tid)
Take action upon notification of ADDBA_REQUEST frame being discarded (likely due to exceeded max retr...
Definition: qos-txop.cc:650
uint8_t GetQosQueueSize(uint8_t tid, Mac48Address receiver) const
Get the value for the Queue Size subfield of the QoS Control field of a QoS data frame of the given T...
Definition: qos-txop.cc:154
void ResetBa(Mac48Address recipient, uint8_t tid)
Reset BA agreement after BA negotiation failed.
Definition: qos-txop.cc:719
Time GetFailedAddBaTimeout() const
Get the timeout for failed BA agreement.
Definition: qos-txop.cc:754
Ptr< WifiMpdu > GetNextMpdu(uint8_t linkId, Ptr< WifiMpdu > peekedItem, WifiTxParameters &txParams, Time availableTime, bool initialFrame)
Prepare the frame to transmit on the given link starting from the MPDU that has been previously peeke...
Definition: qos-txop.cc:474
void CompleteMpduTx(Ptr< WifiMpdu > mpdu)
Stores an MPDU (part of an A-MPDU) in block ack agreement (i.e.
Definition: qos-txop.cc:664
Ptr< WifiMpdu > PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const
Definition: qos-txop.cc:279
Maintains the scoreboard and the receive reordering buffer used by a recipient of a Block Ack agreeme...
void FillBlockAckBitmap(CtrlBAckResponseHeader *blockAckHeader, std::size_t index=0) const
Set the Starting Sequence Number subfield of the Block Ack Starting Sequence Control subfield of the ...
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:568
Introspection did not find any typical Config paths.
Definition: snr-tag.h:35
void Set(double snr)
Set the SNR to the given value.
Definition: snr-tag.cc:84
double Get() const
Return the SNR value.
Definition: snr-tag.cc:90
Status code for association response.
Definition: status-code.h:32
void SetSuccess()
Set success bit to 0 (success).
Definition: status-code.cc:30
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:286
bool IsStrictlyNegative() const
Exactly equivalent to t < 0.
Definition: nstime.h:341
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:314
Time GetTxopLimit() const
Return the TXOP limit.
Definition: txop.cc:473
void UpdateFailedCw(uint8_t linkId)
Update the value of the CW variable for the given link to take into account a transmission failure.
Definition: txop.cc:300
Ptr< WifiMacQueue > GetWifiMacQueue() const
Return the packet queue associated with this Txop.
Definition: txop.cc:220
void ResetCw(uint8_t linkId)
Update the value of the CW variable for the given link to take into account a transmission success or...
Definition: txop.cc:291
virtual void Queue(Ptr< Packet > packet, const WifiMacHeader &hdr)
Definition: txop.cc:505
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
static void SetQosAckPolicy(Ptr< WifiMpdu > item, const WifiAcknowledgment *acknowledgment)
Set the QoS Ack policy for the given MPDU, which must be a QoS data frame.
See IEEE 802.11 chapter 7.3.1.11 Header format: | category: 1 | action value: 1 |.
Definition: mgt-headers.h:1279
void SetAction(CategoryValue type, ActionValue action)
Set action for this Action header.
CategoryValue GetCategory() const
Return the category value.
ActionValue GetAction() const
Return the action value.
Implements the IEEE 802.11 MAC header.
uint8_t GetQosTid() const
Return the Traffic ID of a QoS header.
bool IsBlockAckReq() const
Return true if the header is a BlockAckRequest header.
bool IsCts() const
Return true if the header is a CTS header.
Mac48Address GetAddr1() const
Return the address in the Address 1 field.
uint16_t GetSequenceNumber() const
Return the sequence number of the header.
void SetRetry()
Set the Retry bit in the Frame Control field.
bool IsRetry() const
Return if the Retry bit is set.
bool IsCtl() const
Return true if the Type is Control.
Time GetDuration() const
Return the duration from the Duration/ID field (Time object).
void SetDsNotFrom()
Un-set the From DS bit in the Frame Control field.
bool IsQosEosp() const
Return if the end of service period (EOSP) is set.
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
void SetQosQueueSize(uint8_t size)
Set the Queue Size subfield in the QoS control field.
bool IsBlockAck() const
Return true if the header is a BlockAck header.
void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
Mac48Address GetAddr2() const
Return the address in the Address 2 field.
bool HasData() const
Return true if the header type is DATA and is not DATA_NULL.
QosAckPolicy GetQosAckPolicy() const
Return the QoS Ack policy in the QoS control field.
void SetDuration(Time duration)
Set the Duration/ID field with the given duration (Time object).
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 SetQosEosp()
Set the end of service period (EOSP) bit in the QoS control field.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
void SetDsNotTo()
Un-set the To DS bit in the Frame Control field.
QosAckPolicy
Ack policy for QoS frames.
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition: wifi-mac.cc:1236
TypeOfStation GetTypeOfStation() const
Return the type of station.
Definition: wifi-mac.cc:418
RecipientAgreementOptConstRef GetBaAgreementEstablishedAsRecipient(Mac48Address originator, uint8_t tid) const
Definition: wifi-mac.cc:1297
virtual Ptr< WifiMacQueue > GetTxopQueue(AcIndex ac) const
Get the wifi MAC queue of the (Qos)Txop associated with the given AC, if such (Qos)Txop is installed,...
Definition: wifi-mac.cc:536
OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
Definition: wifi-mac.cc:1283
Mac48Address GetAddress() const
Definition: wifi-mac.cc:443
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:490
void Send(Ptr< const WifiPsdu > psdu, const WifiTxVector &txVector)
This function is a wrapper for the Send variant that accepts a WifiConstPsduMap as first argument.
Definition: wifi-phy.cc:1693
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:786
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:774
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1480
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:996
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1473
std::set< uint8_t > GetTids() const
Get the set of TIDs of the QoS Data frames included in the PSDU.
Definition: wifi-psdu.cc:178
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition: wifi-psdu.cc:279
Time GetDuration() const
Get the duration from the Duration/ID field, which is common to all the MPDUs.
Definition: wifi-psdu.cc:153
std::vector< Ptr< WifiMpdu > >::const_iterator end() const
Return a const iterator to past-the-last MPDU.
Definition: wifi-psdu.cc:345
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition: wifi-psdu.cc:333
Mac48Address GetAddr2() const
Get the Transmitter Address (TA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:128
uint32_t GetSize() const
Return the size of the PSDU in bytes.
Definition: wifi-psdu.cc:273
Ptr< const Packet > GetPayload(std::size_t i) const
Get the payload of the i-th MPDU.
Definition: wifi-psdu.cc:291
Mac48Address GetAddr1() const
Get the Receiver Address (RA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:113
bool IsAggregate() const
Return true if the PSDU is an S-MPDU or A-MPDU.
Definition: wifi-psdu.cc:83
void SetDuration(Time duration)
Set the Duration/ID field on all the MPDUs.
Definition: wifi-psdu.cc:168
std::size_t GetNMpdus() const
Return the number of MPDUs constituting the PSDU.
Definition: wifi-psdu.cc:327
WifiMacHeader::QosAckPolicy GetAckPolicyForTid(uint8_t tid) const
Get the QoS Ack Policy of the QoS Data frames included in the PSDU that have the given TID.
Definition: wifi-psdu.cc:192
void ReportDataFailed(Ptr< const WifiMpdu > mpdu)
Should be invoked whenever the AckTimeout associated to a transmission attempt expires.
void ReportFinalDataFailed(Ptr< const WifiMpdu > mpdu)
Should be invoked after calling ReportDataFailed if NeedRetransmission returns false.
std::optional< Mac48Address > GetAffiliatedStaAddress(const Mac48Address &mldAddress) const
Get the address of the remote station operating on this link and affiliated with the MLD having the g...
void ReportRtsOk(const WifiMacHeader &header, double ctsSnr, WifiMode ctsMode, double rtsSnr)
Should be invoked whenever we receive the CTS associated to an RTS we just sent.
void ReportRxOk(Mac48Address address, RxSignalInfo rxSignalInfo, WifiTxVector txVector)
void ReportAmpduTxStatus(Mac48Address address, uint16_t nSuccessfulMpdus, uint16_t nFailedMpdus, double rxSnr, double dataSnr, WifiTxVector dataTxVector)
Typically called per A-MPDU, either when a Block ACK was successfully received or when a BlockAckTime...
bool GetHtSupported() const
Return whether the device has HT capability support enabled.
bool GetVhtSupported() const
Return whether the device has VHT capability support enabled.
WifiTxVector GetDataTxVector(const WifiMacHeader &header, uint16_t allowedWidth)
std::optional< Mac48Address > GetMldAddress(const Mac48Address &address) const
Get the address of the MLD the given station is affiliated with, if any.
This class stores the TX parameters (TX vector, protection mechanism, acknowledgment mechanism,...
uint32_t GetSizeIfAddMpdu(Ptr< const WifiMpdu > mpdu) const
Get the size in bytes of the frame in case the given MPDU is added.
std::pair< uint32_t, uint32_t > GetSizeIfAggregateMsdu(Ptr< const WifiMpdu > msdu) const
Get the size in bytes of the frame in case the given MSDU is aggregated.
std::unique_ptr< WifiProtection > m_protection
protection method
uint32_t GetSize(Mac48Address receiver) const
Get the size in bytes of the (A-)MPDU addressed to the given receiver.
std::unique_ptr< WifiAcknowledgment > m_acknowledgment
acknowledgment method
const PsduInfo * GetPsduInfo(Mac48Address receiver) const
Get a pointer to the information about the PSDU addressed to the given receiver, if present,...
WifiTxVector m_txVector
TXVECTOR of the frame being prepared.
void AggregateMsdu(Ptr< const WifiMpdu > msdu)
Record that an MSDU is being aggregated to the last MPDU added to the frame that hase the same receiv...
void Clear()
Reset the TX parameters.
bool IsRunning() const
Return true if the timer is running.
void Cancel()
Cancel the timer.
void Set(Reason reason, const Time &delay, MEM mem_ptr, OBJ obj, Args... args)
This method is called when a frame soliciting a response is transmitted.
Reason GetReason() const
Get the reason why the timer was started.
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
WifiMode GetMode(uint16_t staId=SU_STA_ID) const
If this TX vector is associated with an SU PPDU, return the selected payload transmission mode.
void SetAggregation(bool aggregation)
Sets if PSDU contains A-MPDU.
WifiModulationClass GetModulationClass() const
Get the modulation class specified by this TXVECTOR.
#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(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:76
#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_NOARGS()
Output the name of the function.
#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
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
uint8_t GetTid(Ptr< const Packet > packet, const WifiMacHeader hdr)
This function is useful to get traffic id of different packet types.
Definition: qos-utils.cc:195
WifiModulationClass
This enumeration defines the modulation classes per (Table 10-6 "Modulation classes"; IEEE 802....
AcIndex
This enumeration defines the Access Categories as an enumeration with values corresponding to the AC ...
Definition: qos-utils.h:72
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
address
Definition: first.py:40
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:848
Every class exported by the ns3 library is enclosed in the ns3 namespace.
@ STA
Definition: wifi-mac.h:62
uint32_t GetBlockAckRequestSize(BlockAckReqType type)
Return the total BlockAckRequest size (including FCS trailer).
Definition: wifi-utils.cc:76
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_CTL_BACKRESP
@ WIFI_QOSDATA_UNICAST_QUEUE
static constexpr uint16_t SEQNO_SPACE_SIZE
Size of the space of sequence numbers.
Definition: wifi-utils.h:133
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition: wifi-utils.cc:66
std::tuple< WifiContainerQueueType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, Address, TID) identifying a container queue.
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:488
value
Definition: second.py:41
mac
Definition: third.py:85
ns3::Time timeout
RxSignalInfo structure containing info on the received signal.
Definition: phy-entity.h:70
double snr
SNR in linear scale.
Definition: phy-entity.h:71
WifiAcknowledgment is an abstract base struct.
Time acknowledgmentTime
time required by the acknowledgment method
const Method method
acknowledgment method
WifiBarBlockAck specifies that a BlockAckReq is sent to solicit a Block Ack response.
BlockAckType baType
BlockAck type.
WifiTxVector blockAckTxVector
BlockAck TXVECTOR.
WifiTxVector blockAckReqTxVector
BlockAckReq TXVECTOR.
BlockAckReqType barType
BlockAckReq type.
WifiBlockAck specifies that acknowledgment via Block Ack is required.
WifiTxVector blockAckTxVector
BlockAck TXVECTOR.
BlockAckType baType
BlockAck type.
WifiNoProtection specifies that no protection method is used.
information about the frame being prepared for a specific receiver
std::map< uint8_t, std::set< uint16_t > > seqNumbers
set of the sequence numbers of the MPDUs added for each TID
uint32_t ampduSize
the size in bytes of the A-MPDU if multiple MPDUs have been added, and zero otherwise
typedef for union of different ActionValues
Definition: mgt-headers.h:1444
BlockAckActionValue blockAck
block ack
Definition: mgt-headers.h:1446