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/assert.h"
24 #include "ns3/ctrl-headers.h"
25 #include "ns3/log.h"
26 #include "ns3/mgt-action-headers.h"
27 #include "ns3/recipient-block-ack-agreement.h"
28 #include "ns3/snr-tag.h"
29 #include "ns3/sta-wifi-mac.h"
30 #include "ns3/vht-configuration.h"
31 #include "ns3/wifi-mac-queue.h"
32 #include "ns3/wifi-utils.h"
33 
34 #include <array>
35 #include <optional>
36 
37 #undef NS_LOG_APPEND_CONTEXT
38 #define NS_LOG_APPEND_CONTEXT std::clog << "[link=" << +m_linkId << "][mac=" << m_self << "] "
39 
40 namespace ns3
41 {
42 
43 NS_LOG_COMPONENT_DEFINE("HtFrameExchangeManager");
44 
45 NS_OBJECT_ENSURE_REGISTERED(HtFrameExchangeManager);
46 
47 TypeId
49 {
50  static TypeId tid = TypeId("ns3::HtFrameExchangeManager")
52  .AddConstructor<HtFrameExchangeManager>()
53  .SetGroupName("Wifi");
54  return tid;
55 }
56 
58 {
59  NS_LOG_FUNCTION(this);
60  m_msduAggregator = CreateObject<MsduAggregator>();
61  m_mpduAggregator = CreateObject<MpduAggregator>();
62 }
63 
65 {
67 }
68 
69 void
71 {
72  NS_LOG_FUNCTION(this);
73  m_pendingAddBaResp.clear();
74  m_msduAggregator = nullptr;
75  m_mpduAggregator = nullptr;
76  m_psdu = nullptr;
77  m_txParams.Clear();
79 }
80 
81 void
83 {
84  m_msduAggregator->SetWifiMac(mac);
85  m_mpduAggregator->SetWifiMac(mac);
87 }
88 
91 {
92  return m_msduAggregator;
93 }
94 
97 {
98  return m_mpduAggregator;
99 }
100 
103 {
104  return m_mac->GetQosTxop(tid)->GetBaManager();
105 }
106 
107 bool
109 {
110  Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
111  bool establish;
112 
113  if (!m_mac->GetHtSupported(recipient))
114  {
115  establish = false;
116  }
117  else if (auto agreement = qosTxop->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
118  agreement && !agreement->get().IsReset())
119  {
120  establish = false;
121  }
122  else
123  {
124  WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, recipient, tid};
125  uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
126  establish =
127  ((qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
128  (m_mpduAggregator->GetMaxAmpduSize(recipient, tid, WIFI_MOD_CLASS_HT) > 0 &&
129  packets > 1) ||
131  }
132 
133  NS_LOG_FUNCTION(this << recipient << +tid << establish);
134  return establish;
135 }
136 
137 bool
139  uint8_t tid,
140  uint16_t startingSeq,
141  uint16_t timeout,
142  bool immediateBAck,
143  Time availableTime)
144 {
145  NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck
146  << availableTime);
147  NS_LOG_DEBUG("Send ADDBA request to " << dest);
148 
149  WifiMacHeader hdr;
151  // use the remote link address if dest is an MLD address
152  auto addr1 = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(dest);
153  hdr.SetAddr1(addr1 ? *addr1 : dest);
154  hdr.SetAddr2(m_self);
155  hdr.SetAddr3(m_bssid);
156  hdr.SetDsNotTo();
157  hdr.SetDsNotFrom();
158 
159  WifiActionHeader actionHdr;
163 
164  Ptr<Packet> packet = Create<Packet>();
165  // Setting ADDBARequest header
166  MgtAddBaRequestHeader reqHdr;
167  reqHdr.SetAmsduSupport(true);
168  if (immediateBAck)
169  {
170  reqHdr.SetImmediateBlockAck();
171  }
172  else
173  {
174  reqHdr.SetDelayedBlockAck();
175  }
176  reqHdr.SetTid(tid);
177  /* For now we don't use buffer size field in the ADDBA request frame. The recipient
178  * will choose how many packets it can receive under block ack.
179  */
180  reqHdr.SetBufferSize(0);
181  reqHdr.SetTimeout(timeout);
182  // set the starting sequence number for the BA agreement
183  reqHdr.SetStartingSequence(startingSeq);
184 
185  GetBaManager(tid)->CreateOriginatorAgreement(reqHdr, dest);
186 
187  packet->AddHeader(reqHdr);
188  packet->AddHeader(actionHdr);
189 
190  Ptr<WifiMpdu> mpdu = Create<WifiMpdu>(packet, hdr);
191 
192  // get the sequence number for the ADDBA Request management frame
193  uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
194  mpdu->GetHeader().SetSequenceNumber(sequence);
195 
196  WifiTxParameters txParams;
197  txParams.m_txVector =
198  GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
199  if (!TryAddMpdu(mpdu, txParams, availableTime))
200  {
201  NS_LOG_DEBUG("Not enough time to send the ADDBA Request frame");
202  return false;
203  }
204 
205  // Wifi MAC queue scheduler is expected to prioritize management frames
206  m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(mpdu);
207  SendMpduWithProtection(mpdu, txParams);
208  return true;
209 }
210 
211 void
213  Mac48Address originator)
214 {
215  NS_LOG_FUNCTION(this << originator);
216  WifiMacHeader hdr;
218  hdr.SetAddr1(originator);
219  hdr.SetAddr2(m_self);
220  hdr.SetAddr3(m_bssid);
221  hdr.SetDsNotFrom();
222  hdr.SetDsNotTo();
223 
224  MgtAddBaResponseHeader respHdr;
225  StatusCode code;
226  code.SetSuccess();
227  respHdr.SetStatusCode(code);
228  // Here a control about queues type?
229  respHdr.SetAmsduSupport(reqHdr->IsAmsduSupported());
230 
231  if (reqHdr->IsImmediateBlockAck())
232  {
233  respHdr.SetImmediateBlockAck();
234  }
235  else
236  {
237  respHdr.SetDelayedBlockAck();
238  }
239  auto tid = reqHdr->GetTid();
240  respHdr.SetTid(tid);
241 
242  auto bufferSize = std::min(m_mac->GetMpduBufferSize(), m_mac->GetMaxBaBufferSize(originator));
243  respHdr.SetBufferSize(bufferSize);
244  respHdr.SetTimeout(reqHdr->GetTimeout());
245 
246  WifiActionHeader actionHdr;
250 
251  Ptr<Packet> packet = Create<Packet>();
252  packet->AddHeader(respHdr);
253  packet->AddHeader(actionHdr);
254 
255  // Get the MLD address of the originator, if an ML setup was performed
256  if (auto originatorMld = GetWifiRemoteStationManager()->GetMldAddress(originator))
257  {
258  originator = *originatorMld;
259  }
260  bool htSupported = GetWifiRemoteStationManager()->GetHtSupported() &&
261  GetWifiRemoteStationManager()->GetHtSupported(originator);
262  GetBaManager(tid)->CreateRecipientAgreement(respHdr,
263  originator,
264  reqHdr->GetStartingSequence(),
265  htSupported,
266  m_rxMiddle);
267 
268  auto agreement = GetBaManager(tid)->GetAgreementAsRecipient(originator, tid);
269  NS_ASSERT(agreement);
270  if (respHdr.GetTimeout() != 0)
271  {
272  Time timeout = MicroSeconds(1024 * agreement->get().GetTimeout());
273 
274  agreement->get().m_inactivityEvent =
277  this,
278  originator,
279  tid,
280  false);
281  }
282 
283  auto mpdu = Create<WifiMpdu>(packet, hdr);
284 
285  /*
286  * It is possible (though, unlikely) that at this point there are other ADDBA_RESPONSE frame(s)
287  * in the MAC queue. This may happen if the recipient receives an ADDBA_REQUEST frame, enqueues
288  * an ADDBA_RESPONSE frame, but is not able to successfully transmit it before the timer to
289  * wait for ADDBA_RESPONSE expires at the originator. The latter may then send another
290  * ADDBA_REQUEST frame, which triggers the creation of another ADDBA_RESPONSE frame.
291  * To avoid sending unnecessary ADDBA_RESPONSE frames, we keep track of the previously enqueued
292  * ADDBA_RESPONSE frame (if any), dequeue it and replace it with the new ADDBA_RESPONSE frame.
293  */
294 
295  // remove any pending ADDBA_RESPONSE frame
296  AgreementKey key(originator, tid);
297  if (auto it = m_pendingAddBaResp.find(key); it != m_pendingAddBaResp.end())
298  {
299  NS_ASSERT_MSG(it->second, "The pointer to the pending ADDBA_RESPONSE cannot be null");
300  DequeueMpdu(it->second);
301  m_pendingAddBaResp.erase(it);
302  }
303  // store the new ADDBA_RESPONSE frame
304  m_pendingAddBaResp[key] = mpdu;
305 
306  // It is unclear which queue this frame should go into. For now we
307  // bung it into the queue corresponding to the TID for which we are
308  // establishing an agreement, and push it to the head.
309  // Wifi MAC queue scheduler is expected to prioritize management frames
310  m_mac->GetQosTxop(tid)->Queue(mpdu);
311 }
312 
313 void
314 HtFrameExchangeManager::SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator)
315 {
316  NS_LOG_FUNCTION(this << addr << +tid << byOriginator);
317  WifiMacHeader hdr;
319  // use the remote link address if addr is an MLD address
320  hdr.SetAddr1(GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr).value_or(addr));
321  hdr.SetAddr2(m_self);
322  hdr.SetAddr3(m_bssid);
323  hdr.SetDsNotTo();
324  hdr.SetDsNotFrom();
325 
326  MgtDelBaHeader delbaHdr;
327  delbaHdr.SetTid(tid);
328  byOriginator ? delbaHdr.SetByOriginator() : delbaHdr.SetByRecipient();
329 
330  WifiActionHeader actionHdr;
334 
335  Ptr<Packet> packet = Create<Packet>();
336  packet->AddHeader(delbaHdr);
337  packet->AddHeader(actionHdr);
338 
339  m_mac->GetQosTxop(tid)->Queue(Create<WifiMpdu>(packet, hdr));
340 }
341 
342 bool
343 HtFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
344 {
345  NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
346 
347  // First, check if there is a BAR to be transmitted
348  if (auto mpdu = GetBar(edca->GetAccessCategory());
349  mpdu && SendMpduFromBaManager(mpdu, availableTime, initialFrame))
350  {
351  return true;
352  }
353 
354  Ptr<WifiMpdu> peekedItem = edca->PeekNextMpdu(m_linkId);
355 
356  // Even though channel access is requested when the queue is not empty, at
357  // the time channel access is granted the lifetime of the packet might be
358  // expired and the queue might be empty.
359  if (!peekedItem)
360  {
361  NS_LOG_DEBUG("No frames available for transmission");
362  return false;
363  }
364 
365  const WifiMacHeader& hdr = peekedItem->GetHeader();
366  // setup a Block Ack agreement if needed
367  if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() &&
368  NeedSetupBlockAck(hdr.GetAddr1(), hdr.GetQosTid()))
369  {
370  // if the peeked MPDU has been already transmitted, use its sequence number
371  // as the starting sequence number for the BA agreement, otherwise use the
372  // next available sequence number
373  uint16_t startingSeq =
374  (hdr.IsRetry()
375  ? hdr.GetSequenceNumber()
376  : m_txMiddle->GetNextSeqNumberByTidAndAddress(hdr.GetQosTid(), hdr.GetAddr1()));
377  return SendAddBaRequest(hdr.GetAddr1(),
378  hdr.GetQosTid(),
379  startingSeq,
381  true,
382  availableTime);
383  }
384 
385  // Use SendDataFrame if we can try aggregation
386  if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() && !peekedItem->IsFragment() &&
387  !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem =
388  CreateAliasIfNeeded(peekedItem)))
389  {
390  return SendDataFrame(peekedItem, availableTime, initialFrame);
391  }
392 
393  // Use the QoS FEM to transmit the frame in all the other cases, i.e.:
394  // - the frame is not a QoS data frame
395  // - the frame is a broadcast QoS data frame
396  // - the frame is a fragment
397  // - the frame must be fragmented
398  return QosFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
399 }
400 
403  std::optional<uint8_t> optTid,
404  std::optional<Mac48Address> optAddress)
405 {
406  NS_LOG_FUNCTION(this << +ac << optTid.has_value() << optAddress.has_value());
407  NS_ASSERT_MSG(optTid.has_value() == optAddress.has_value(),
408  "Either both or none of TID and address must be provided");
409 
410  // remove all expired MPDUs from the MAC queue, so that
411  // BlockAckRequest frames (if needed) are scheduled
412  auto queue = m_mac->GetTxopQueue(ac);
413  queue->WipeAllExpiredMpdus();
414 
415  Ptr<WifiMpdu> bar;
416  Ptr<WifiMpdu> prevBar;
417  Ptr<WifiMpdu> selectedBar;
418 
419  // we could iterate over all the scheduler's queues and ignore those that do not contain
420  // control frames, but it's more efficient to peek frames until we get frames that are
421  // not control frames, given that control frames have the highest priority
422  while ((bar = queue->PeekFirstAvailable(m_linkId, prevBar)) && bar && bar->GetHeader().IsCtl())
423  {
424  if (bar->GetHeader().IsBlockAckReq())
425  {
426  CtrlBAckRequestHeader reqHdr;
427  bar->GetPacket()->PeekHeader(reqHdr);
428  auto tid = reqHdr.GetTidInfo();
429  Mac48Address recipient = bar->GetHeader().GetAddr1();
430  auto recipientMld = m_mac->GetMldAddress(recipient);
431 
432  // the scheduler should not return a BlockAckReq that cannot be sent on this link:
433  // either the TA address is the address of this link or it is the MLD address and
434  // the RA field is the MLD address of a device we can communicate with on this link
435  NS_ASSERT_MSG(bar->GetHeader().GetAddr2() == m_self ||
436  (bar->GetHeader().GetAddr2() == m_mac->GetAddress() && recipientMld &&
437  GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)),
438  "Cannot use link " << +m_linkId << " to send BAR: " << *bar);
439 
440  if (optAddress &&
441  (GetWifiRemoteStationManager()->GetMldAddress(*optAddress).value_or(*optAddress) !=
442  GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient) ||
443  optTid != tid))
444  {
445  NS_LOG_DEBUG("BAR " << *bar
446  << " cannot be returned because it is not addressed"
447  " to the given station for the given TID");
448  prevBar = bar;
449  continue;
450  }
451 
452  auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid);
453  if (!agreement)
454  {
455  NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
456  << " was torn down");
457  queue->Remove(bar);
458  continue;
459  }
460  // update BAR if the starting sequence number changed
461  if (auto seqNo = agreement->get().GetStartingSequence();
462  reqHdr.GetStartingSequence() != seqNo)
463  {
464  reqHdr.SetStartingSequence(seqNo);
465  Ptr<Packet> packet = Create<Packet>();
466  packet->AddHeader(reqHdr);
467  auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader(), bar->GetTimestamp());
468  queue->Replace(bar, updatedBar);
469  bar = updatedBar;
470  }
471  // bar is the BlockAckReq to send
472  selectedBar = bar;
473 
474  // if the selected BAR is intended to be sent on this specific link and the recipient
475  // is an MLD, remove the BAR (if any) for this BA agreement that can be sent on any
476  // link (because a BAR that can be sent on any link to a recipient is no longer
477  // needed after sending a BAR to that recipient on this link)
478  if (bar->GetHeader().GetAddr2() == m_self && recipientMld)
479  {
481  WIFI_UNICAST,
482  *recipientMld,
483  std::nullopt};
484  Ptr<WifiMpdu> otherBar;
485  while ((otherBar = queue->PeekByQueueId(queueId, otherBar)))
486  {
487  if (otherBar->GetHeader().IsBlockAckReq())
488  {
489  CtrlBAckRequestHeader otherReqHdr;
490  otherBar->GetPacket()->PeekHeader(otherReqHdr);
491  if (otherReqHdr.GetTidInfo() == tid)
492  {
493  queue->Remove(otherBar);
494  break;
495  }
496  }
497  }
498  }
499  break;
500  }
501  if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
502  {
503  return bar;
504  }
505  // not a BAR nor a Trigger Frame, continue
506  prevBar = bar;
507  }
508 
509  if (!selectedBar)
510  {
511  // check if we can send a BAR to a recipient to which a BAR can only be sent if data queued
512  auto baManager = m_mac->GetQosTxop(ac)->GetBaManager();
513  for (const auto& [recipient, tid] : baManager->GetSendBarIfDataQueuedList())
514  {
515  if (queue->PeekByTidAndAddress(tid, recipient))
516  {
517  auto [reqHdr, hdr] = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
518  auto pkt = Create<Packet>();
519  pkt->AddHeader(reqHdr);
520  selectedBar = Create<WifiMpdu>(pkt, hdr);
521  baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
522  queue->Enqueue(selectedBar);
523  break;
524  }
525  }
526  }
527 
528  if (selectedBar && selectedBar->GetHeader().GetAddr2() != m_self)
529  {
530  // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
531  // and move to the appropriate container queue
532  NS_ASSERT(selectedBar->GetHeader().GetAddr2() == m_mac->GetAddress());
533  DequeueMpdu(selectedBar);
534  const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
535  auto addr1 =
536  GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(currAddr1);
537  selectedBar->GetHeader().SetAddr1(addr1);
538  selectedBar->GetHeader().SetAddr2(m_self);
539  queue->Enqueue(selectedBar);
540  }
541 
542  return selectedBar;
543 }
544 
545 bool
547  Time availableTime,
548  bool initialFrame)
549 {
550  NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
551 
552  // First, check if there is a BAR to be transmitted
553  if (!mpdu->GetHeader().IsBlockAckReq())
554  {
555  NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
556  return false;
557  }
558 
559  // Prepare the TX parameters. Note that the default ack manager expects the
560  // data TxVector in the m_txVector field to compute the BlockAck TxVector.
561  // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
562  // a few lines below.
563  WifiTxParameters txParams;
564  txParams.m_txVector =
565  GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
566 
567  if (!TryAddMpdu(mpdu, txParams, availableTime))
568  {
569  NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
570  return false;
571  }
572 
574 
575  // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
576  auto blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
577  txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
578 
579  // we can transmit the BlockAckReq frame
580  SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
581  return true;
582 }
583 
584 bool
586  Time availableTime,
587  bool initialFrame)
588 {
589  NS_ASSERT(peekedItem && peekedItem->GetHeader().IsQosData() &&
590  !peekedItem->GetHeader().GetAddr1().IsBroadcast() && !peekedItem->IsFragment());
591  NS_LOG_FUNCTION(this << *peekedItem << availableTime << initialFrame);
592 
593  Ptr<QosTxop> edca = m_mac->GetQosTxop(peekedItem->GetHeader().GetQosTid());
594  WifiTxParameters txParams;
595  txParams.m_txVector =
596  GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
597  Ptr<WifiMpdu> mpdu =
598  edca->GetNextMpdu(m_linkId, peekedItem, txParams, availableTime, initialFrame);
599 
600  if (!mpdu)
601  {
602  NS_LOG_DEBUG("Not enough time to transmit a frame");
603  return false;
604  }
605 
606  // try A-MPDU aggregation
607  std::vector<Ptr<WifiMpdu>> mpduList =
608  m_mpduAggregator->GetNextAmpdu(mpdu, txParams, availableTime);
609  NS_ASSERT(txParams.m_acknowledgment);
610 
611  if (mpduList.size() > 1)
612  {
613  // A-MPDU aggregation succeeded
614  SendPsduWithProtection(Create<WifiPsdu>(std::move(mpduList)), txParams);
615  }
616  else if (txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
617  {
618  // a QoS data frame using the Block Ack policy can be followed by a BlockAckReq
619  // frame and a BlockAck frame. Such a sequence is handled by the HT FEM
620  SendPsduWithProtection(Create<WifiPsdu>(mpdu, false), txParams);
621  }
622  else
623  {
624  // transmission can be handled by the base FEM
625  SendMpduWithProtection(mpdu, txParams);
626  }
627 
628  return true;
629 }
630 
631 void
633 {
634  NS_LOG_FUNCTION(this << acknowledgment);
635  NS_ASSERT(acknowledgment);
636 
637  if (acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
638  {
639  auto blockAcknowledgment = static_cast<WifiBlockAck*>(acknowledgment);
640  Time baTxDuration = m_phy->CalculateTxDuration(GetBlockAckSize(blockAcknowledgment->baType),
641  blockAcknowledgment->blockAckTxVector,
642  m_phy->GetPhyBand());
643  blockAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + baTxDuration;
644  }
645  else if (acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
646  {
647  auto barBlockAcknowledgment = static_cast<WifiBarBlockAck*>(acknowledgment);
648  Time barTxDuration =
649  m_phy->CalculateTxDuration(GetBlockAckRequestSize(barBlockAcknowledgment->barType),
650  barBlockAcknowledgment->blockAckReqTxVector,
651  m_phy->GetPhyBand());
652  Time baTxDuration =
653  m_phy->CalculateTxDuration(GetBlockAckSize(barBlockAcknowledgment->baType),
654  barBlockAcknowledgment->blockAckTxVector,
655  m_phy->GetPhyBand());
656  barBlockAcknowledgment->acknowledgmentTime =
657  2 * m_phy->GetSifs() + barTxDuration + baTxDuration;
658  }
659  else
660  {
662  }
663 }
664 
665 void
667 {
668  ForwardPsduDown(GetWifiPsdu(mpdu, txVector), txVector);
669 }
670 
673 {
674  return Create<WifiPsdu>(mpdu, false);
675 }
676 
677 void
679 {
680  NS_LOG_FUNCTION(this << *mpdu);
681 
682  if (mpdu->GetHeader().IsQosData())
683  {
684  uint8_t tid = mpdu->GetHeader().GetQosTid();
685  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
686 
687  if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
688  {
689  // notify the BA manager that the MPDU was acknowledged
690  edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
691  }
692  }
693  else if (mpdu->GetHeader().IsAction())
694  {
695  auto addr1 = mpdu->GetHeader().GetAddr1();
696  auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
697  WifiActionHeader actionHdr;
698  Ptr<Packet> p = mpdu->GetPacket()->Copy();
699  p->RemoveHeader(actionHdr);
700  if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
701  {
703  {
704  MgtDelBaHeader delBa;
705  p->PeekHeader(delBa);
706  auto tid = delBa.GetTid();
707  if (delBa.IsByOriginator())
708  {
709  GetBaManager(tid)->DestroyOriginatorAgreement(address, tid);
710  }
711  else
712  {
713  GetBaManager(tid)->DestroyRecipientAgreement(address, tid);
714  }
715  }
717  {
718  // Setup ADDBA response timeout
719  MgtAddBaRequestHeader addBa;
720  p->PeekHeader(addBa);
721  Ptr<QosTxop> edca = m_mac->GetQosTxop(addBa.GetTid());
724  edca,
725  address,
726  addBa.GetTid());
727  }
729  {
730  // A recipient Block Ack agreement must exist
732  p->PeekHeader(addBa);
733  auto tid = addBa.GetTid();
734  NS_ASSERT_MSG(GetBaManager(tid)->GetAgreementAsRecipient(address, tid),
735  "Recipient BA agreement {" << address << ", " << +tid
736  << "} not found");
737  m_pendingAddBaResp.erase({address, tid});
738  }
739  }
740  }
742 }
743 
744 void
746 {
747  NS_LOG_DEBUG(this);
748 
750  {
751  // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
752  // be transmitted (as responses) the following within the current TXOP:
753  // f) Any number of BlockAckReq frames
754  // (Sec. 10.22.2.8 of 802.11-2016)
755  NS_LOG_DEBUG("Schedule a transmission from Block Ack Manager in a SIFS");
758 
759  // TXOP limit is null, hence the txopDuration parameter is unused
760  Simulator::Schedule(m_phy->GetSifs(), fp, this, m_edca, Seconds(0));
761  }
762  else
763  {
765  }
766 }
767 
768 void
770 {
771  NS_LOG_FUNCTION(this << *mpdu);
772 
773  if (mpdu->GetHeader().IsQosData())
774  {
775  GetBaManager(mpdu->GetHeader().GetQosTid())->NotifyDiscardedMpdu(mpdu);
776  }
777  else if (mpdu->GetHeader().IsAction())
778  {
779  WifiActionHeader actionHdr;
780  mpdu->GetPacket()->PeekHeader(actionHdr);
781  if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
783  {
784  uint8_t tid = GetTid(mpdu->GetPacket(), mpdu->GetHeader());
785  auto recipient = mpdu->GetHeader().GetAddr1();
786  // if the recipient is an MLD, use its MLD address
787  if (auto mldAddr = GetWifiRemoteStationManager()->GetMldAddress(recipient))
788  {
789  recipient = *mldAddr;
790  }
791  if (auto agreement = GetBaManager(tid)->GetAgreementAsOriginator(recipient, tid);
792  agreement && agreement->get().IsPending())
793  {
794  NS_LOG_DEBUG("No ACK after ADDBA request");
795  Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
796  qosTxop->NotifyOriginatorAgreementNoReply(recipient, tid);
799  qosTxop,
800  recipient,
801  tid);
802  }
803  }
804  }
806 }
807 
808 void
810 {
811  NS_LOG_FUNCTION(this << *mpdu);
812 
813  if (mpdu->GetHeader().IsQosData())
814  {
815  uint8_t tid = mpdu->GetHeader().GetQosTid();
816  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
817 
818  if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
819  {
820  // notify the BA manager that the MPDU was not acknowledged
821  edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
822  return;
823  }
824  }
826 }
827 
828 void
830 {
831  NS_LOG_FUNCTION(this << *psdu);
832 
833  auto tids = psdu->GetTids();
834 
835  if (tids.empty() || // no QoS data frames included
836  !m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin()))
837  {
839  return;
840  }
841 
842  // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
843  auto mpduIt = psdu->end();
844 
845  do
846  {
847  std::advance(mpduIt, -1);
848 
849  const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
850  if (hdr.IsQosData())
851  {
852  uint8_t tid = hdr.GetQosTid();
854 
855  if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
856  {
857  // The MPDU has never been transmitted, so we can make its sequence
858  // number available again if it is the highest sequence number
859  // assigned by the MAC TX middle
860  uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
861 
862  if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
863  {
864  (*mpduIt)->UnassignSeqNo();
865  m_txMiddle->SetSequenceNumberFor(&hdr);
866 
867  NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
868  << ", next sequence "
869  "number for dest="
870  << hdr.GetAddr1() << ",tid=" << +tid << " is "
871  << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
872  }
873  }
874  }
875  } while (mpduIt != psdu->begin());
876 }
877 
878 Time
880 {
881  NS_LOG_FUNCTION(this << txDuration << &txParams);
882 
883  NS_ASSERT(m_edca);
884 
886  {
887  NS_ASSERT(txParams.m_acknowledgment &&
888  txParams.m_acknowledgment->acknowledgmentTime != Time::Min());
889  return txParams.m_acknowledgment->acknowledgmentTime;
890  }
891 
892  // under multiple protection settings, if the TXOP limit is not null, Duration/ID
893  // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
894  // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
895  // of 802.11-2016)
896  return std::max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
897 }
898 
899 void
901 {
902  NS_LOG_FUNCTION(this << psdu << &txParams);
903 
904  m_psdu = psdu;
905  m_txParams = std::move(txParams);
906 
907 #ifdef NS3_BUILD_PROFILE_DEBUG
908  // If protection is required, the MPDUs must be stored in some queue because
909  // they are not put back in a queue if the RTS/CTS exchange fails
911  {
912  for (const auto& mpdu : *PeekPointer(m_psdu))
913  {
914  NS_ASSERT(mpdu->GetHeader().IsCtl() || mpdu->IsQueued());
915  }
916  }
917 #endif
918 
919  // Make sure that the acknowledgment time has been computed, so that SendRts()
920  // and SendCtsToSelf() can reuse this value.
922 
923  if (m_txParams.m_acknowledgment->acknowledgmentTime == Time::Min())
924  {
926  }
927 
928  // Set QoS Ack policy
930 
931  for (const auto& mpdu : *PeekPointer(m_psdu))
932  {
933  if (mpdu->IsQueued())
934  {
935  mpdu->SetInFlight(m_linkId);
936  }
937  }
938 
940 }
941 
942 void
944 {
945  NS_LOG_FUNCTION(this);
946  if (m_psdu)
947  {
949  m_sentRtsTo.clear();
950  SendPsdu();
951  return;
952  }
954 }
955 
956 void
958 {
959  NS_LOG_FUNCTION(this << *rts << txVector);
960 
961  if (!m_psdu)
962  {
963  // A CTS Timeout occurred when protecting a single MPDU is handled by the
964  // parent classes
966  return;
967  }
968 
970  m_psdu = nullptr;
971 }
972 
973 void
975 {
976  NS_LOG_FUNCTION(this);
977 
978  Time txDuration =
980 
982 
984  {
986 
987  std::set<uint8_t> tids = m_psdu->GetTids();
988  NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
989 
990  if (tids.empty() || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
991  {
992  // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
994  }
995  }
997  {
999 
1000  // the timeout duration is "aSIFSTime + aSlotTime + aRxPHYStartDelay, starting
1001  // at the PHY-TXEND.confirm primitive" (section 10.3.2.9 or 10.22.2.2 of 802.11-2016).
1002  // aRxPHYStartDelay equals the time to transmit the PHY header.
1003  auto blockAcknowledgment = static_cast<WifiBlockAck*>(m_txParams.m_acknowledgment.get());
1004 
1005  Time timeout =
1006  txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
1007  m_phy->CalculatePhyPreambleAndHeaderDuration(blockAcknowledgment->blockAckTxVector);
1010  timeout,
1011  {m_psdu->GetAddr1()},
1013  this,
1014  m_psdu,
1017  }
1019  {
1021 
1022  // schedule the transmission of a BAR in a SIFS
1023  std::set<uint8_t> tids = m_psdu->GetTids();
1024  NS_ABORT_MSG_IF(tids.size() > 1,
1025  "Acknowledgment method incompatible with a Multi-TID A-MPDU");
1026  uint8_t tid = *tids.begin();
1027 
1028  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1029  auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid);
1030  GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1031 
1033  }
1034  else
1035  {
1036  NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
1037  << m_txParams.m_acknowledgment.get() << ")");
1038  }
1039 
1040  // transmit the PSDU
1041  if (m_psdu->GetNMpdus() > 1)
1042  {
1044  }
1045  else
1046  {
1048  }
1049 
1051  {
1052  // we are done in case the A-MPDU does not require acknowledgment
1053  m_psdu = nullptr;
1054  }
1055 }
1056 
1057 void
1059 {
1060  NS_LOG_FUNCTION(this << psdu);
1061 
1062  for (const auto& mpdu : *PeekPointer(psdu))
1063  {
1064  auto& hdr = mpdu->GetHeader();
1065 
1066  if (hdr.IsQosData() && hdr.HasData())
1067  {
1068  auto tid = hdr.GetQosTid();
1069  m_mac->GetQosTxop(tid)->CompleteMpduTx(mpdu);
1070  }
1071  }
1072 }
1073 
1074 void
1076 {
1077  NS_LOG_FUNCTION(this << psdu);
1078 
1079  // use an array to avoid computing the queue size for every MPDU in the PSDU
1080  std::array<std::optional<uint8_t>, 8> queueSizeForTid;
1081 
1082  for (const auto& mpdu : *PeekPointer(psdu))
1083  {
1084  WifiMacHeader& hdr = mpdu->GetHeader();
1085 
1086  if (hdr.IsQosData())
1087  {
1088  uint8_t tid = hdr.GetQosTid();
1089  auto edca = m_mac->GetQosTxop(tid);
1090 
1091  if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
1092  {
1093  // set the Queue Size subfield of the QoS Control field
1094  if (!queueSizeForTid[tid].has_value())
1095  {
1096  queueSizeForTid[tid] = edca->GetQosQueueSize(tid, hdr.GetAddr1());
1097  }
1098 
1099  hdr.SetQosEosp();
1100  hdr.SetQosQueueSize(queueSizeForTid[tid].value());
1101  }
1102  }
1103  }
1104 
1106 }
1107 
1108 void
1110 {
1111  NS_LOG_DEBUG(this << psdu);
1112 
1113  for (const auto& mpdu : *PeekPointer(psdu))
1114  {
1115  DequeueMpdu(mpdu);
1116  }
1117 }
1118 
1119 void
1121 {
1122  NS_LOG_FUNCTION(this << psdu << txVector);
1123 
1124  NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
1125  FinalizeMacHeader(psdu);
1126  NotifyTxToEdca(psdu);
1128 
1129  if (psdu->IsAggregate())
1130  {
1131  txVector.SetAggregation(true);
1132  }
1133 
1134  m_phy->Send(psdu, txVector);
1135 }
1136 
1137 bool
1139  const WifiTxParameters& txParams,
1140  Time ppduDurationLimit) const
1141 {
1142  NS_ASSERT(mpdu);
1143  NS_LOG_FUNCTION(this << *mpdu << &txParams << ppduDurationLimit);
1144 
1145  Mac48Address receiver = mpdu->GetHeader().GetAddr1();
1146  uint32_t ampduSize = txParams.GetSizeIfAddMpdu(mpdu);
1147 
1148  if (txParams.GetSize(receiver) > 0)
1149  {
1150  // we are attempting to perform A-MPDU aggregation, hence we have to check
1151  // that we meet the limit on the max A-MPDU size
1152  uint8_t tid;
1153  const WifiTxParameters::PsduInfo* info;
1154 
1155  if (mpdu->GetHeader().IsQosData())
1156  {
1157  tid = mpdu->GetHeader().GetQosTid();
1158  }
1159  else if ((info = txParams.GetPsduInfo(receiver)) && !info->seqNumbers.empty())
1160  {
1161  tid = info->seqNumbers.begin()->first;
1162  }
1163  else
1164  {
1165  NS_ABORT_MSG("Cannot aggregate a non-QoS data frame to an A-MPDU that does"
1166  " not contain any QoS data frame");
1167  }
1168 
1169  WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1170 
1171  if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1172  {
1173  return false;
1174  }
1175  }
1176 
1177  return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1178 }
1179 
1180 bool
1182  Mac48Address receiver,
1183  uint8_t tid,
1184  WifiModulationClass modulation) const
1185 {
1186  NS_LOG_FUNCTION(this << ampduSize << receiver << +tid << modulation);
1187 
1188  uint32_t maxAmpduSize = m_mpduAggregator->GetMaxAmpduSize(receiver, tid, modulation);
1189 
1190  if (maxAmpduSize == 0)
1191  {
1192  NS_LOG_DEBUG("A-MPDU aggregation disabled");
1193  return false;
1194  }
1195 
1196  if (ampduSize > maxAmpduSize)
1197  {
1198  NS_LOG_DEBUG("the frame does not meet the constraint on max A-MPDU size (" << maxAmpduSize
1199  << ")");
1200  return false;
1201  }
1202  return true;
1203 }
1204 
1205 bool
1207  WifiTxParameters& txParams,
1208  Time availableTime) const
1209 {
1210  NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1211  NS_LOG_FUNCTION(this << *msdu << &txParams << availableTime);
1212 
1213  // check if aggregating the given MSDU requires a different protection method
1214  NS_ASSERT(txParams.m_protection);
1215  Time protectionTime = txParams.m_protection->protectionTime;
1216 
1217  std::unique_ptr<WifiProtection> protection;
1218  protection = GetProtectionManager()->TryAggregateMsdu(msdu, txParams);
1219  bool protectionSwapped = false;
1220 
1221  if (protection)
1222  {
1223  // the protection method has changed, calculate the new protection time
1224  CalculateProtectionTime(protection.get());
1225  protectionTime = protection->protectionTime;
1226  // swap unique pointers, so that the txParams that is passed to the next
1227  // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1228  txParams.m_protection.swap(protection);
1229  protectionSwapped = true;
1230  }
1231  NS_ASSERT(protectionTime != Time::Min());
1232 
1233  // check if aggregating the given MSDU requires a different acknowledgment method
1234  NS_ASSERT(txParams.m_acknowledgment);
1235  Time acknowledgmentTime = txParams.m_acknowledgment->acknowledgmentTime;
1236 
1237  std::unique_ptr<WifiAcknowledgment> acknowledgment;
1238  acknowledgment = GetAckManager()->TryAggregateMsdu(msdu, txParams);
1239  bool acknowledgmentSwapped = false;
1240 
1241  if (acknowledgment)
1242  {
1243  // the acknowledgment method has changed, calculate the new acknowledgment time
1244  CalculateAcknowledgmentTime(acknowledgment.get());
1245  acknowledgmentTime = acknowledgment->acknowledgmentTime;
1246  // swap unique pointers, so that the txParams that is passed to the next
1247  // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1248  txParams.m_acknowledgment.swap(acknowledgment);
1249  acknowledgmentSwapped = true;
1250  }
1251  NS_ASSERT(acknowledgmentTime != Time::Min());
1252 
1253  Time ppduDurationLimit = Time::Min();
1254  if (availableTime != Time::Min())
1255  {
1256  ppduDurationLimit = availableTime - protectionTime - acknowledgmentTime;
1257  }
1258 
1259  if (!IsWithinLimitsIfAggregateMsdu(msdu, txParams, ppduDurationLimit))
1260  {
1261  // adding MPDU failed, restore protection and acknowledgment methods
1262  // if they were swapped
1263  if (protectionSwapped)
1264  {
1265  txParams.m_protection.swap(protection);
1266  }
1267  if (acknowledgmentSwapped)
1268  {
1269  txParams.m_acknowledgment.swap(acknowledgment);
1270  }
1271  return false;
1272  }
1273 
1274  // the given MPDU can be added, hence update the txParams
1275  txParams.AggregateMsdu(msdu);
1276  UpdateTxDuration(msdu->GetHeader().GetAddr1(), txParams);
1277 
1278  return true;
1279 }
1280 
1281 bool
1283  const WifiTxParameters& txParams,
1284  Time ppduDurationLimit) const
1285 {
1286  NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1287  NS_LOG_FUNCTION(this << *msdu << &txParams << ppduDurationLimit);
1288 
1289  std::pair<uint16_t, uint32_t> ret = txParams.GetSizeIfAggregateMsdu(msdu);
1290  Mac48Address receiver = msdu->GetHeader().GetAddr1();
1291  uint8_t tid = msdu->GetHeader().GetQosTid();
1292  WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1293 
1294  // Check that the limit on A-MSDU size is met
1295  uint16_t maxAmsduSize = m_msduAggregator->GetMaxAmsduSize(receiver, tid, modulation);
1296 
1297  if (maxAmsduSize == 0)
1298  {
1299  NS_LOG_DEBUG("A-MSDU aggregation disabled");
1300  return false;
1301  }
1302 
1303  if (ret.first > maxAmsduSize)
1304  {
1305  NS_LOG_DEBUG("No other MSDU can be aggregated: maximum A-MSDU size (" << maxAmsduSize
1306  << ") reached ");
1307  return false;
1308  }
1309 
1310  const WifiTxParameters::PsduInfo* info = txParams.GetPsduInfo(msdu->GetHeader().GetAddr1());
1311  NS_ASSERT(info);
1312 
1313  if (info->ampduSize > 0)
1314  {
1315  // the A-MSDU being built is aggregated to other MPDUs in an A-MPDU.
1316  // Check that the limit on A-MPDU size is met.
1317  if (!IsWithinAmpduSizeLimit(ret.second, receiver, tid, modulation))
1318  {
1319  return false;
1320  }
1321  }
1322 
1323  return IsWithinSizeAndTimeLimits(ret.second, receiver, txParams, ppduDurationLimit);
1324 }
1325 
1326 void
1328 {
1329  NS_LOG_FUNCTION(this << *psdu << txVector);
1330 
1331  GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1332 
1333  bool resetCw;
1334  MissedBlockAck(psdu, txVector, resetCw);
1335 
1336  NS_ASSERT(m_edca);
1337 
1338  if (resetCw)
1339  {
1341  }
1342  else
1343  {
1345  }
1346 
1347  m_psdu = nullptr;
1349 }
1350 
1351 void
1353  const WifiTxVector& txVector,
1354  bool& resetCw)
1355 {
1356  NS_LOG_FUNCTION(this << psdu << txVector << resetCw);
1357 
1358  auto recipient = psdu->GetAddr1();
1359  auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
1360  bool isBar;
1361  uint8_t tid;
1362 
1363  if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
1364  {
1365  isBar = true;
1366  CtrlBAckRequestHeader baReqHdr;
1367  psdu->GetPayload(0)->PeekHeader(baReqHdr);
1368  tid = baReqHdr.GetTidInfo();
1369  }
1370  else
1371  {
1372  isBar = false;
1373  std::set<uint8_t> tids = psdu->GetTids();
1374  NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
1375  NS_ASSERT(!tids.empty());
1376  tid = *tids.begin();
1377  }
1378 
1379  Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1380 
1381  if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
1382  {
1383  // we have to send a BlockAckReq, if needed
1384  if (GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld))
1385  {
1386  NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
1397  if (isBar)
1398  {
1399  psdu->GetHeader(0).SetRetry();
1400  }
1401  else
1402  {
1403  // missed block ack after data frame with Implicit BAR Ack policy
1404  auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid);
1405  GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1406  }
1407  resetCw = false;
1408  }
1409  else
1410  {
1411  NS_LOG_DEBUG("Missed Block Ack, do not transmit a BlockAckReq");
1412  // if a BA agreement exists, we can get here if there is no outstanding
1413  // MPDU whose lifetime has not expired yet.
1414  GetWifiRemoteStationManager()->ReportFinalDataFailed(*psdu->begin());
1415  if (isBar)
1416  {
1417  DequeuePsdu(psdu);
1418  }
1419  if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
1420  {
1421  // schedule a BlockAckRequest to be sent only if there are data frames queued
1422  // for this recipient
1423  GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
1424  }
1425  resetCw = true;
1426  }
1427  }
1428  else
1429  {
1430  // we have to retransmit the data frames, if needed
1432  ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
1433  if (!GetWifiRemoteStationManager()->NeedRetransmission(*psdu->begin()))
1434  {
1435  NS_LOG_DEBUG("Missed Block Ack, do not retransmit the data frames");
1436  GetWifiRemoteStationManager()->ReportFinalDataFailed(*psdu->begin());
1437  for (const auto& mpdu : *PeekPointer(psdu))
1438  {
1439  NotifyPacketDiscarded(mpdu);
1440  DequeueMpdu(mpdu);
1441  }
1442  resetCw = true;
1443  }
1444  else
1445  {
1446  NS_LOG_DEBUG("Missed Block Ack, retransmit data frames");
1447  GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
1448  resetCw = false;
1449  }
1450  }
1451 }
1452 
1453 void
1455  Time durationId,
1456  WifiTxVector& blockAckTxVector,
1457  double rxSnr)
1458 {
1459  NS_LOG_FUNCTION(this << durationId << blockAckTxVector << rxSnr);
1460 
1461  WifiMacHeader hdr;
1463  auto addr1 = agreement.GetPeer();
1464  if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
1465  {
1466  addr1 = *originator;
1467  }
1468  hdr.SetAddr1(addr1);
1469  hdr.SetAddr2(m_self);
1470  hdr.SetDsNotFrom();
1471  hdr.SetDsNotTo();
1472 
1473  CtrlBAckResponseHeader blockAck;
1474  blockAck.SetType(agreement.GetBlockAckType());
1475  blockAck.SetTidInfo(agreement.GetTid());
1476  agreement.FillBlockAckBitmap(&blockAck);
1477 
1478  Ptr<Packet> packet = Create<Packet>();
1479  packet->AddHeader(blockAck);
1480  Ptr<WifiPsdu> psdu = GetWifiPsdu(Create<WifiMpdu>(packet, hdr), blockAckTxVector);
1481 
1482  // 802.11-2016, Section 9.2.5.7: In a BlockAck frame transmitted in response
1483  // to a BlockAckReq frame or transmitted in response to a frame containing an
1484  // implicit block ack request, the Duration/ID field is set to the value obtained
1485  // from the Duration/ ID field of the frame that elicited the response minus the
1486  // time, in microseconds between the end of the PPDU carrying the frame that
1487  // elicited the response and the end of the PPDU carrying the BlockAck frame.
1488  Time baDurationId = durationId - m_phy->GetSifs() -
1489  m_phy->CalculateTxDuration(psdu, blockAckTxVector, m_phy->GetPhyBand());
1490  // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016)
1491  if (baDurationId.IsStrictlyNegative())
1492  {
1493  baDurationId = Seconds(0);
1494  }
1495  psdu->GetHeader(0).SetDuration(baDurationId);
1496 
1497  SnrTag tag;
1498  tag.Set(rxSnr);
1499  psdu->GetPayload(0)->AddPacketTag(tag);
1500 
1501  ForwardPsduDown(psdu, blockAckTxVector);
1502 }
1503 
1504 void
1506  RxSignalInfo rxSignalInfo,
1507  const WifiTxVector& txVector,
1508  bool inAmpdu)
1509 {
1510  // The received MPDU is either broadcast or addressed to this station
1511  NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1512 
1513  double rxSnr = rxSignalInfo.snr;
1514  const WifiMacHeader& hdr = mpdu->GetHeader();
1515 
1516  if (hdr.IsCtl())
1517  {
1518  if (hdr.IsCts() && m_txTimer.IsRunning() &&
1520  {
1521  NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1522  NS_ASSERT(hdr.GetAddr1() == m_self);
1523 
1524  Mac48Address sender = m_psdu->GetAddr1();
1525  NS_LOG_DEBUG("Received CTS from=" << sender);
1526 
1527  SnrTag tag;
1528  mpdu->GetPacket()->PeekPacketTag(tag);
1529  GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1530  GetWifiRemoteStationManager()->ReportRtsOk(m_psdu->GetHeader(0),
1531  rxSnr,
1532  txVector.GetMode(),
1533  tag.Get());
1534 
1535  m_txTimer.Cancel();
1539  this);
1540  }
1541  else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1543  {
1544  Mac48Address sender = hdr.GetAddr2();
1545  NS_LOG_DEBUG("Received BlockAck from=" << sender);
1546 
1547  SnrTag tag;
1548  mpdu->GetPacket()->PeekPacketTag(tag);
1549 
1550  // notify the Block Ack Manager
1551  CtrlBAckResponseHeader blockAck;
1552  mpdu->GetPacket()->PeekHeader(blockAck);
1553  uint8_t tid = blockAck.GetTidInfo();
1554  std::pair<uint16_t, uint16_t> ret =
1555  GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
1556  blockAck,
1557  m_mac->GetMldAddress(sender).value_or(sender),
1558  {tid});
1559  GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1560  ret.first,
1561  ret.second,
1562  rxSnr,
1563  tag.Get(),
1565 
1566  // cancel the timer
1567  m_txTimer.Cancel();
1569 
1570  // Reset the CW
1572 
1573  // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
1574  if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
1575  {
1577  }
1578  m_psdu = nullptr;
1580  }
1581  else if (hdr.IsBlockAckReq())
1582  {
1583  NS_ASSERT(hdr.GetAddr1() == m_self);
1584  NS_ABORT_MSG_IF(inAmpdu, "BlockAckReq in A-MPDU is not supported");
1585 
1586  Mac48Address sender = hdr.GetAddr2();
1587  NS_LOG_DEBUG("Received BlockAckReq from=" << sender);
1588 
1589  CtrlBAckRequestHeader blockAckReq;
1590  mpdu->GetPacket()->PeekHeader(blockAckReq);
1591  NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1592  uint8_t tid = blockAckReq.GetTidInfo();
1593 
1594  auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(sender, tid);
1595 
1596  if (!agreement)
1597  {
1598  NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1599  return;
1600  }
1601 
1602  GetBaManager(tid)->NotifyGotBlockAckRequest(
1603  m_mac->GetMldAddress(sender).value_or(sender),
1604  tid,
1605  blockAckReq.GetStartingSequence());
1606 
1607  NS_LOG_DEBUG("Schedule Block Ack");
1609  m_phy->GetSifs(),
1611  this,
1612  *agreement,
1613  hdr.GetDuration(),
1614  GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
1615  rxSnr);
1616  }
1617  else
1618  {
1619  // the received control frame cannot be handled here
1620  QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1621  }
1622  return;
1623  }
1624 
1625  if (hdr.IsQosData() && hdr.HasData() && hdr.GetAddr1() == m_self)
1626  {
1627  uint8_t tid = hdr.GetQosTid();
1628 
1630  {
1631  // a Block Ack agreement has been established
1632  NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
1633 
1634  GetBaManager(tid)->NotifyGotMpdu(mpdu);
1635 
1636  if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
1637  {
1638  NS_LOG_DEBUG("Schedule Normal Ack");
1641  this,
1642  hdr,
1643  txVector,
1644  rxSnr);
1645  }
1646  return;
1647  }
1648  // We let the QosFrameExchangeManager handle QoS data frame not belonging
1649  // to a Block Ack agreement
1650  }
1651 
1652  QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1653 }
1654 
1655 void
1657  const RxSignalInfo& rxSignalInfo,
1658  const WifiTxVector& txVector,
1659  const std::vector<bool>& perMpduStatus)
1660 {
1661  std::set<uint8_t> tids = psdu->GetTids();
1662 
1663  // Multi-TID A-MPDUs are not supported yet
1664  if (tids.size() == 1)
1665  {
1666  uint8_t tid = *tids.begin();
1667  WifiMacHeader::QosAckPolicy ackPolicy = psdu->GetAckPolicyForTid(tid);
1668  NS_ASSERT(psdu->GetNMpdus() > 1);
1669 
1670  if (ackPolicy == WifiMacHeader::NORMAL_ACK)
1671  {
1672  // Normal Ack or Implicit Block Ack Request
1673  NS_LOG_DEBUG("Schedule Block Ack");
1674  auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
1675  NS_ASSERT(agreement);
1676 
1678  m_phy->GetSifs(),
1680  this,
1681  *agreement,
1682  psdu->GetDuration(),
1683  GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
1684  rxSignalInfo.snr);
1685  }
1686  }
1687 }
1688 
1689 } // namespace ns3
#define min(a, b)
Definition: 80211b.c:41
#define max(a, b)
Definition: 80211b.c:42
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.
std::set< Mac48Address > m_sentRtsTo
the STA(s) which we sent an RTS to (waiting for CTS)
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
virtual void StartProtection(const WifiTxParameters &txParams)
Start the protection mechanism indicated by the given TX parameters.
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
std::set< Mac48Address > m_protectedStas
STAs that have replied to an RTS in this TXOP.
virtual void RetransmitMpduAfterMissedAck(Ptr< WifiMpdu > mpdu) const
Retransmit an MPDU that was not acknowledged.
virtual void ProtectionCompleted()
Transmit prepared frame upon successful protection mechanism.
virtual void NotifyReceivedNormalAck(Ptr< WifiMpdu > mpdu)
Notify other components that an MPDU was acknowledged.
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)
virtual void FinalizeMacHeader(Ptr< const WifiPsdu > psdu)
Finalize the MAC header of the MPDUs in the given PSDU before transmission.
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 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 FinalizeMacHeader(Ptr< const WifiPsdu > psdu) override
Finalize the MAC header of the MPDUs in the given PSDU before transmission.
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 ProtectionCompleted() override
Transmit prepared frame upon successful protection mechanism.
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...
bool SendAddBaRequest(Mac48Address recipient, uint8_t tid, uint16_t startingSeq, uint16_t timeout, bool immediateBAck, Time availableTime)
Sends an ADDBA Request to establish a block ack agreement with STA addressed by recipient for TID tid...
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.
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.
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.
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:960
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...
bool TryAddMpdu(Ptr< const WifiMpdu > mpdu, WifiTxParameters &txParams, Time availableTime) const
Recompute the protection and acknowledgment methods to use if the given MPDU is added to the frame be...
void DoDispose() override
Destructor implementation.
Ptr< BlockAckManager > GetBaManager()
Get the Block Ack Manager associated with this QosTxop.
Definition: qos-txop.cc:286
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:378
bool UseExplicitBarAfterMissedBlockAck() const
Return true if an explicit BlockAckRequest is sent after a missed BlockAck.
Definition: qos-txop.cc:327
Time GetAddBaResponseTimeout() const
Get the timeout for ADDBA response.
Definition: qos-txop.cc:775
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition: qos-txop.cc:800
void AddBaResponseTimeout(Mac48Address recipient, uint8_t tid)
Callback when ADDBA response is not received after timeout.
Definition: qos-txop.cc:740
uint8_t GetBlockAckThreshold() const
Return the current threshold for block ack mechanism.
Definition: qos-txop.cc:727
uint16_t GetBlockAckInactivityTimeout() const
Get the BlockAck inactivity timeout.
Definition: qos-txop.cc:734
virtual Time GetRemainingTxop(uint8_t linkId) const
Return the remaining duration in the current TXOP on the given link.
Definition: qos-txop.cc:636
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:691
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:179
void ResetBa(Mac48Address recipient, uint8_t tid)
Reset BA agreement after BA negotiation failed.
Definition: qos-txop.cc:753
Time GetFailedAddBaTimeout() const
Get the timeout for failed BA agreement.
Definition: qos-txop.cc:788
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:495
void CompleteMpduTx(Ptr< WifiMpdu > mpdu)
Stores an MPDU (part of an A-MPDU) in block ack agreement (i.e.
Definition: qos-txop.cc:698
std::pair< CtrlBAckRequestHeader, WifiMacHeader > PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const
Definition: qos-txop.cc:304
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:571
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:287
bool IsStrictlyNegative() const
Exactly equivalent to t < 0.
Definition: nstime.h:342
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
Time GetTxopLimit() const
Return the TXOP limit.
Definition: txop.cc:490
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:312
Ptr< WifiMacQueue > GetWifiMacQueue() const
Return the packet queue associated with this Txop.
Definition: txop.cc:231
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:303
virtual void Queue(Ptr< Packet > packet, const WifiMacHeader &hdr)
Definition: txop.cc:523
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
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 |.
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.
virtual 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:1632
TypeOfStation GetTypeOfStation() const
Return the type of station.
Definition: wifi-mac.cc:422
uint16_t GetMpduBufferSize() const
Definition: wifi-mac.cc:1862
bool GetHtSupported() const
Return whether the device supports HT.
Definition: wifi-mac.cc:1761
RecipientAgreementOptConstRef GetBaAgreementEstablishedAsRecipient(Mac48Address originator, uint8_t tid) const
Definition: wifi-mac.cc:1693
uint16_t GetMaxBaBufferSize(std::optional< Mac48Address > address=std::nullopt) const
Get the maximum Block Ack buffer size (in number of MPDUs) supported by the given device,...
Definition: wifi-mac.cc:1838
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:545
Ptr< VhtConfiguration > GetVhtConfiguration() const
Definition: wifi-mac.cc:1743
OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
Definition: wifi-mac.cc:1679
Mac48Address GetAddress() const
Definition: wifi-mac.cc:452
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:499
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:1720
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:793
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:781
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1507
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1021
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1500
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
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, const std::set< Mac48Address > &from, 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.
uint16_t GetChannelWidth() const
#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:1350
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
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:191
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:73
@ STA
Definition: wifi-mac.h:65
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
address
Definition: first.py:47
void(* Time)(Time oldValue, Time newValue)
TracedValue callback signature for Time.
Definition: nstime.h:839
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::tuple< WifiContainerQueueType, WifiReceiverAddressType, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
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
static constexpr uint16_t SEQNO_SPACE_SIZE
Size of the space of sequence numbers.
Definition: wifi-utils.h:185
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition: wifi-utils.cc:66
U * PeekPointer(const Ptr< U > &p)
Definition: ptr.h:449
mac
Definition: third.py:92
ns3::Time timeout
RxSignalInfo structure containing info on the received signal.
Definition: phy-entity.h:69
double snr
SNR in linear scale.
Definition: phy-entity.h:70
WifiAcknowledgment is an abstract base struct.
const Method method
acknowledgment method
WifiBarBlockAck specifies that a BlockAckReq is sent to solicit a Block Ack response.
WifiBlockAck specifies that acknowledgment via Block Ack is required.
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
BlockAckActionValue blockAck
block ack