A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Author: Stefano Avallone <stavallo@unina.it>
7 */
8
10
11#include "ht-configuration.h"
12
13#include "ns3/abort.h"
14#include "ns3/assert.h"
15#include "ns3/ctrl-headers.h"
16#include "ns3/log.h"
17#include "ns3/mgt-action-headers.h"
18#include "ns3/recipient-block-ack-agreement.h"
19#include "ns3/snr-tag.h"
20#include "ns3/sta-wifi-mac.h"
21#include "ns3/vht-configuration.h"
22#include "ns3/wifi-mac-queue.h"
23#include "ns3/wifi-net-device.h"
24#include "ns3/wifi-utils.h"
25
26#include <array>
27#include <optional>
28
29#undef NS_LOG_APPEND_CONTEXT
30#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
31
32namespace ns3
33{
34
35NS_LOG_COMPONENT_DEFINE("HtFrameExchangeManager");
36
37NS_OBJECT_ENSURE_REGISTERED(HtFrameExchangeManager);
38
39TypeId
41{
42 static TypeId tid = TypeId("ns3::HtFrameExchangeManager")
44 .AddConstructor<HtFrameExchangeManager>()
45 .SetGroupName("Wifi");
46 return tid;
47}
48
55
60
61void
72
73void
80
86
92
95{
96 return m_mac->GetQosTxop(tid)->GetBaManager();
97}
98
99bool
101{
102 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
103 bool establish;
104
105 // NOLINTBEGIN(bugprone-branch-clone)
106 if (!m_mac->GetHtConfiguration() ||
107 (!GetWifiRemoteStationManager()->GetHtSupported(recipient) &&
108 !GetWifiRemoteStationManager()->GetStationHe6GhzCapabilities(recipient)))
109 {
110 // no Block Ack if this device or the recipient are not HT STAs and do not operate
111 // in the 6 GHz band
112 establish = false;
113 }
114 else if (auto agreement = qosTxop->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
115 agreement && !agreement->get().IsReset())
116 {
117 // Block Ack agreement already established
118 establish = false;
119 }
120 // NOLINTEND(bugprone-branch-clone)
121 else
122 {
123 WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, recipient, tid};
124 uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
125 establish =
126 (m_mac->Is6GhzBand(m_linkId) ||
127 (qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
128 (m_mpduAggregator->GetMaxAmpduSize(recipient, tid, WIFI_MOD_CLASS_HT) > 0 &&
129 packets > 1) ||
130 m_mac->GetVhtConfiguration());
131 }
132
133 NS_LOG_FUNCTION(this << recipient << +tid << establish);
134 return establish;
135}
136
137bool
139 uint8_t tid,
140 uint16_t startingSeq,
141 uint16_t timeout,
142 bool immediateBAck,
143 Time availableTime,
144 std::optional<Mac48Address> gcrGroupAddr)
145{
146 NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck << availableTime
147 << gcrGroupAddr.has_value());
148 NS_LOG_DEBUG("Send ADDBA request to " << dest);
149
150 WifiMacHeader hdr;
152 // use the remote link address if dest is an MLD address
153 auto addr1 = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(dest);
154 hdr.SetAddr1(addr1 ? *addr1 : dest);
155 hdr.SetAddr2(m_self);
156 hdr.SetAddr3(m_bssid);
157 hdr.SetDsNotTo();
158 hdr.SetDsNotFrom();
159
160 WifiActionHeader actionHdr;
163 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
164
165 Ptr<Packet> packet = Create<Packet>();
166 // Setting ADDBARequest header
168 reqHdr.SetAmsduSupport(true);
169 if (immediateBAck)
170 {
171 reqHdr.SetImmediateBlockAck();
172 }
173 else
174 {
175 reqHdr.SetDelayedBlockAck();
176 }
177 reqHdr.SetTid(tid);
178 /* For now we don't use buffer size field in the ADDBA request frame. The recipient
179 * will choose how many packets it can receive under block ack.
180 */
181 reqHdr.SetBufferSize(0);
182 reqHdr.SetTimeout(timeout);
183 // set the starting sequence number for the BA agreement
184 reqHdr.SetStartingSequence(startingSeq);
185
186 if (gcrGroupAddr)
187 {
188 reqHdr.SetGcrGroupAddress(*gcrGroupAddr);
189 }
190
191 GetBaManager(tid)->CreateOriginatorAgreement(reqHdr, dest);
192
193 packet->AddHeader(reqHdr);
194 packet->AddHeader(actionHdr);
195
196 Ptr<WifiMpdu> mpdu = Create<WifiMpdu>(packet, hdr);
197
198 // get the sequence number for the ADDBA Request management frame
199 uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
200 mpdu->GetHeader().SetSequenceNumber(sequence);
201
202 WifiTxParameters txParams;
203 txParams.m_txVector =
204 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
205 if (!TryAddMpdu(mpdu, txParams, availableTime))
206 {
207 NS_LOG_DEBUG("Not enough time to send the ADDBA Request frame");
208 return false;
209 }
210
211 // Wifi MAC queue scheduler is expected to prioritize management frames
212 m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(mpdu);
213 SendMpduWithProtection(mpdu, txParams);
214 return true;
215}
216
217void
219 Mac48Address originator)
220{
221 NS_LOG_FUNCTION(this << originator);
222 WifiMacHeader hdr;
224 hdr.SetAddr1(originator);
225 hdr.SetAddr2(m_self);
226 hdr.SetAddr3(m_bssid);
227 hdr.SetDsNotFrom();
228 hdr.SetDsNotTo();
229
231 StatusCode code;
232 code.SetSuccess();
233 respHdr.SetStatusCode(code);
234 // Here a control about queues type?
235 respHdr.SetAmsduSupport(reqHdr.IsAmsduSupported());
236
237 if (reqHdr.IsImmediateBlockAck())
238 {
239 respHdr.SetImmediateBlockAck();
240 }
241 else
242 {
243 respHdr.SetDelayedBlockAck();
244 }
245 auto tid = reqHdr.GetTid();
246 respHdr.SetTid(tid);
247
248 auto bufferSize = std::min(m_mac->GetMpduBufferSize(), m_mac->GetMaxBaBufferSize(originator));
249 respHdr.SetBufferSize(bufferSize);
250 respHdr.SetTimeout(reqHdr.GetTimeout());
251
252 if (auto gcrGroupAddr = reqHdr.GetGcrGroupAddress())
253 {
254 respHdr.SetGcrGroupAddress(*gcrGroupAddr);
255 }
256
257 WifiActionHeader actionHdr;
260 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
261
262 Ptr<Packet> packet = Create<Packet>();
263 packet->AddHeader(respHdr);
264 packet->AddHeader(actionHdr);
265
266 // Get the MLD address of the originator, if an ML setup was performed
267 if (auto originatorMld = GetWifiRemoteStationManager()->GetMldAddress(originator))
268 {
269 originator = *originatorMld;
270 }
271 GetBaManager(tid)->CreateRecipientAgreement(respHdr,
272 originator,
273 reqHdr.GetStartingSequence(),
274 m_rxMiddle);
275
276 auto agreement = GetBaManager(tid)->GetAgreementAsRecipient(originator, tid);
277 NS_ASSERT(agreement);
278 if (respHdr.GetTimeout() != 0)
279 {
280 Time timeout = MicroSeconds(1024 * agreement->get().GetTimeout());
281
282 agreement->get().m_inactivityEvent =
285 this,
286 originator,
287 tid,
288 false);
289 }
290
291 auto mpdu = Create<WifiMpdu>(packet, hdr);
292
293 /*
294 * It is possible (though, unlikely) that at this point there are other ADDBA_RESPONSE frame(s)
295 * in the MAC queue. This may happen if the recipient receives an ADDBA_REQUEST frame, enqueues
296 * an ADDBA_RESPONSE frame, but is not able to successfully transmit it before the timer to
297 * wait for ADDBA_RESPONSE expires at the originator. The latter may then send another
298 * ADDBA_REQUEST frame, which triggers the creation of another ADDBA_RESPONSE frame.
299 * To avoid sending unnecessary ADDBA_RESPONSE frames, we keep track of the previously enqueued
300 * ADDBA_RESPONSE frame (if any), dequeue it and replace it with the new ADDBA_RESPONSE frame.
301 */
302
303 // remove any pending ADDBA_RESPONSE frame
304 AgreementKey key(originator, tid);
305 if (auto it = m_pendingAddBaResp.find(key); it != m_pendingAddBaResp.end())
306 {
307 NS_ASSERT_MSG(it->second, "The pointer to the pending ADDBA_RESPONSE cannot be null");
308 DequeueMpdu(it->second);
309 m_pendingAddBaResp.erase(it);
310 }
311 // store the new ADDBA_RESPONSE frame
312 m_pendingAddBaResp[key] = mpdu;
313
314 // It is unclear which queue this frame should go into. For now we
315 // bung it into the queue corresponding to the TID for which we are
316 // establishing an agreement, and push it to the head.
317 // Wifi MAC queue scheduler is expected to prioritize management frames
318 m_mac->GetQosTxop(tid)->Queue(mpdu);
319}
320
321void
322HtFrameExchangeManager::SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator)
323{
324 NS_LOG_FUNCTION(this << addr << +tid << byOriginator);
325 WifiMacHeader hdr;
327 // use the remote link address if addr is an MLD address
328 hdr.SetAddr1(GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr).value_or(addr));
329 hdr.SetAddr2(m_self);
330 hdr.SetAddr3(m_bssid);
331 hdr.SetDsNotTo();
332 hdr.SetDsNotFrom();
333
334 MgtDelBaHeader delbaHdr;
335 delbaHdr.SetTid(tid);
336 byOriginator ? delbaHdr.SetByOriginator() : delbaHdr.SetByRecipient();
337
338 WifiActionHeader actionHdr;
340 action.blockAck = WifiActionHeader::BLOCK_ACK_DELBA;
341 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
342
343 Ptr<Packet> packet = Create<Packet>();
344 packet->AddHeader(delbaHdr);
345 packet->AddHeader(actionHdr);
346
347 m_mac->GetQosTxop(tid)->Queue(Create<WifiMpdu>(packet, hdr));
348}
349
350bool
351HtFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
352{
353 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
354
355 // First, check if there is a BAR to be transmitted
356 if (auto mpdu = GetBar(edca->GetAccessCategory());
357 mpdu && SendMpduFromBaManager(mpdu, availableTime, initialFrame))
358 {
359 return true;
360 }
361
362 Ptr<WifiMpdu> peekedItem = edca->PeekNextMpdu(m_linkId);
363
364 // Even though channel access is requested when the queue is not empty, at
365 // the time channel access is granted the lifetime of the packet might be
366 // expired and the queue might be empty.
367 if (!peekedItem)
368 {
369 NS_LOG_DEBUG("No frames available for transmission");
370 return false;
371 }
372
373 const WifiMacHeader& hdr = peekedItem->GetHeader();
374 // setup a Block Ack agreement if needed
375 if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() &&
377 {
378 // if the peeked MPDU has been already transmitted, use its sequence number
379 // as the starting sequence number for the BA agreement, otherwise use the
380 // next available sequence number
381 uint16_t startingSeq =
382 (hdr.IsRetry()
383 ? hdr.GetSequenceNumber()
384 : m_txMiddle->GetNextSeqNumberByTidAndAddress(hdr.GetQosTid(), hdr.GetAddr1()));
385 return SendAddBaRequest(hdr.GetAddr1(),
386 hdr.GetQosTid(),
387 startingSeq,
388 edca->GetBlockAckInactivityTimeout(),
389 true,
390 availableTime);
391 }
392
393 // Use SendDataFrame if we can try aggregation
394 if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() && !peekedItem->IsFragment() &&
395 !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem =
396 CreateAliasIfNeeded(peekedItem)))
397 {
398 return SendDataFrame(peekedItem, availableTime, initialFrame);
399 }
400
401 // Use the QoS FEM to transmit the frame in all the other cases, i.e.:
402 // - the frame is not a QoS data frame
403 // - the frame is a broadcast QoS data frame
404 // - the frame is a fragment
405 // - the frame must be fragmented
406 return QosFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
407}
408
411 std::optional<uint8_t> optTid,
412 std::optional<Mac48Address> optAddress)
413{
414 NS_LOG_FUNCTION(this << +ac << optTid.has_value() << optAddress.has_value());
415 NS_ASSERT_MSG(optTid.has_value() == optAddress.has_value(),
416 "Either both or none of TID and address must be provided");
417
418 // remove all expired MPDUs from the MAC queue, so that
419 // BlockAckRequest frames (if needed) are scheduled
420 auto queue = m_mac->GetTxopQueue(ac);
421 queue->WipeAllExpiredMpdus();
422
423 Ptr<WifiMpdu> bar;
424 Ptr<WifiMpdu> prevBar;
425 Ptr<WifiMpdu> selectedBar;
426
427 // we could iterate over all the scheduler's queues and ignore those that do not contain
428 // control frames, but it's more efficient to peek frames until we get frames that are
429 // not control frames, given that control frames have the highest priority
430 while ((bar = queue->PeekFirstAvailable(m_linkId, prevBar)) && bar && bar->GetHeader().IsCtl())
431 {
432 if (bar->GetHeader().IsBlockAckReq())
433 {
435 bar->GetPacket()->PeekHeader(reqHdr);
436 auto tid = reqHdr.GetTidInfo();
437 Mac48Address recipient = bar->GetHeader().GetAddr1();
438 auto recipientMld = m_mac->GetMldAddress(recipient);
439
440 // the scheduler should not return a BlockAckReq that cannot be sent on this link:
441 // either the TA address is the address of this link or it is the MLD address and
442 // the RA field is the MLD address of a device we can communicate with on this link
443 NS_ASSERT_MSG(bar->GetHeader().GetAddr2() == m_self ||
444 (bar->GetHeader().GetAddr2() == m_mac->GetAddress() && recipientMld &&
445 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)),
446 "Cannot use link " << +m_linkId << " to send BAR: " << *bar);
447
448 if (optAddress &&
449 (GetWifiRemoteStationManager()->GetMldAddress(*optAddress).value_or(*optAddress) !=
450 GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient) ||
451 optTid != tid))
452 {
453 NS_LOG_DEBUG("BAR " << *bar
454 << " cannot be returned because it is not addressed"
455 " to the given station for the given TID");
456 prevBar = bar;
457 continue;
458 }
459
460 auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid);
461 if (!agreement)
462 {
463 NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
464 << " was torn down");
465 queue->Remove(bar);
466 continue;
467 }
468 // update BAR if the starting sequence number changed
469 if (auto seqNo = agreement->get().GetStartingSequence();
470 reqHdr.GetStartingSequence() != seqNo)
471 {
472 reqHdr.SetStartingSequence(seqNo);
473 Ptr<Packet> packet = Create<Packet>();
474 packet->AddHeader(reqHdr);
475 auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader(), bar->GetTimestamp());
476 queue->Replace(bar, updatedBar);
477 bar = updatedBar;
478 }
479 // bar is the BlockAckReq to send
480 selectedBar = bar;
481
482 // if the selected BAR is intended to be sent on this specific link and the recipient
483 // is an MLD, remove the BAR (if any) for this BA agreement that can be sent on any
484 // link (because a BAR that can be sent on any link to a recipient is no longer
485 // needed after sending a BAR to that recipient on this link)
486 if (bar->GetHeader().GetAddr2() == m_self && recipientMld)
487 {
490 *recipientMld,
491 std::nullopt};
492 Ptr<WifiMpdu> otherBar;
493 while ((otherBar = queue->PeekByQueueId(queueId, otherBar)))
494 {
495 if (otherBar->GetHeader().IsBlockAckReq())
496 {
497 CtrlBAckRequestHeader otherReqHdr;
498 otherBar->GetPacket()->PeekHeader(otherReqHdr);
499 if (otherReqHdr.GetTidInfo() == tid)
500 {
501 queue->Remove(otherBar);
502 break;
503 }
504 }
505 }
506 }
507 break;
508 }
509 if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
510 {
511 return bar;
512 }
513 // not a BAR nor a Trigger Frame, continue
514 prevBar = bar;
515 }
516
517 if (!selectedBar)
518 {
519 // check if we can send a BAR to a recipient to which a BAR can only be sent if data queued
520 auto baManager = m_mac->GetQosTxop(ac)->GetBaManager();
521 for (const auto& [recipient, tid] : baManager->GetSendBarIfDataQueuedList())
522 {
523 WifiContainerQueueId queueId(
526 GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient),
527 tid);
528 // check if data is queued and can be transmitted on this link
529 if (queue->PeekByTidAndAddress(tid, recipient) &&
530 !m_mac->GetTxBlockedOnLink(QosUtilsMapTidToAc(tid), queueId, m_linkId))
531 {
532 auto [reqHdr, hdr] = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
533 auto pkt = Create<Packet>();
534 pkt->AddHeader(reqHdr);
535 selectedBar = Create<WifiMpdu>(pkt, hdr);
536 baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
537 queue->Enqueue(selectedBar);
538 break;
539 }
540 }
541 }
542
543 if (selectedBar && selectedBar->GetHeader().GetAddr2() != m_self)
544 {
545 // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
546 // and move to the appropriate container queue
547 NS_ASSERT(selectedBar->GetHeader().GetAddr2() == m_mac->GetAddress());
548 DequeueMpdu(selectedBar);
549 const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
550 auto addr1 =
551 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(currAddr1);
552 selectedBar->GetHeader().SetAddr1(addr1);
553 selectedBar->GetHeader().SetAddr2(m_self);
554 queue->Enqueue(selectedBar);
555 }
556
557 return selectedBar;
558}
559
560bool
562 Time availableTime,
563 bool initialFrame)
564{
565 NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
566
567 // First, check if there is a BAR to be transmitted
568 if (!mpdu->GetHeader().IsBlockAckReq())
569 {
570 NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
571 return false;
572 }
573
574 // Prepare the TX parameters. Note that the default ack manager expects the
575 // data TxVector in the m_txVector field to compute the BlockAck TxVector.
576 // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
577 // a few lines below.
578 WifiTxParameters txParams;
579 txParams.m_txVector =
580 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
581
582 if (!TryAddMpdu(mpdu, txParams, availableTime))
583 {
584 NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
585 return false;
586 }
587
589
590 // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
591 auto blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
592 txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
593
594 // we can transmit the BlockAckReq frame
595 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
596 return true;
597}
598
599bool
601 Time availableTime,
602 bool initialFrame)
603{
604 NS_ASSERT(peekedItem && peekedItem->GetHeader().IsQosData() &&
605 !peekedItem->GetHeader().GetAddr1().IsBroadcast() && !peekedItem->IsFragment());
606 NS_LOG_FUNCTION(this << *peekedItem << availableTime << initialFrame);
607
608 Ptr<QosTxop> edca = m_mac->GetQosTxop(peekedItem->GetHeader().GetQosTid());
609 WifiTxParameters txParams;
610 txParams.m_txVector =
611 GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
612 Ptr<WifiMpdu> mpdu =
613 edca->GetNextMpdu(m_linkId, peekedItem, txParams, availableTime, initialFrame);
614
615 if (!mpdu)
616 {
617 NS_LOG_DEBUG("Not enough time to transmit a frame");
618 return false;
619 }
620
621 // try A-MPDU aggregation
622 std::vector<Ptr<WifiMpdu>> mpduList =
623 m_mpduAggregator->GetNextAmpdu(mpdu, txParams, availableTime);
624 NS_ASSERT(txParams.m_acknowledgment);
625
626 if (mpduList.size() > 1)
627 {
628 // A-MPDU aggregation succeeded
629 SendPsduWithProtection(Create<WifiPsdu>(std::move(mpduList)), txParams);
630 }
631 else if (txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
632 {
633 // a QoS data frame using the Block Ack policy can be followed by a BlockAckReq
634 // frame and a BlockAck frame. Such a sequence is handled by the HT FEM
635 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
636 }
637 else
638 {
639 // transmission can be handled by the base FEM
640 SendMpduWithProtection(mpdu, txParams);
641 }
642
643 return true;
644}
645
646void
648{
649 NS_LOG_FUNCTION(this << acknowledgment);
650 NS_ASSERT(acknowledgment);
651
652 if (acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
653 {
654 auto blockAcknowledgment = static_cast<WifiBlockAck*>(acknowledgment);
655 Time baTxDuration =
656 WifiPhy::CalculateTxDuration(GetBlockAckSize(blockAcknowledgment->baType),
657 blockAcknowledgment->blockAckTxVector,
658 m_phy->GetPhyBand());
659 blockAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + baTxDuration;
660 }
661 else if (acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
662 {
663 auto barBlockAcknowledgment = static_cast<WifiBarBlockAck*>(acknowledgment);
664 Time barTxDuration =
665 WifiPhy::CalculateTxDuration(GetBlockAckRequestSize(barBlockAcknowledgment->barType),
666 barBlockAcknowledgment->blockAckReqTxVector,
667 m_phy->GetPhyBand());
668 Time baTxDuration =
669 WifiPhy::CalculateTxDuration(GetBlockAckSize(barBlockAcknowledgment->baType),
670 barBlockAcknowledgment->blockAckTxVector,
671 m_phy->GetPhyBand());
672 barBlockAcknowledgment->acknowledgmentTime =
673 2 * m_phy->GetSifs() + barTxDuration + baTxDuration;
674 }
675 else
676 {
678 }
679}
680
681void
683{
684 ForwardPsduDown(GetWifiPsdu(mpdu, txVector), txVector);
685}
686
689{
690 return Create<WifiPsdu>(mpdu, false);
691}
692
693void
695{
696 NS_LOG_FUNCTION(this << *mpdu);
697
698 if (mpdu->GetHeader().IsQosData())
699 {
700 uint8_t tid = mpdu->GetHeader().GetQosTid();
701 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
702
703 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
704 {
705 // notify the BA manager that the MPDU was acknowledged
706 edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
707 // the BA manager fires the AckedMpdu trace source, so nothing else must be done
708 return;
709 }
710 }
711 else if (mpdu->GetHeader().IsAction())
712 {
713 auto addr1 = mpdu->GetHeader().GetAddr1();
714 auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
715 WifiActionHeader actionHdr;
716 Ptr<Packet> p = mpdu->GetPacket()->Copy();
717 p->RemoveHeader(actionHdr);
718 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
719 {
721 {
722 MgtDelBaHeader delBa;
723 p->PeekHeader(delBa);
724 auto tid = delBa.GetTid();
725 if (delBa.IsByOriginator())
726 {
727 GetBaManager(tid)->DestroyOriginatorAgreement(address, tid);
728 }
729 else
730 {
731 GetBaManager(tid)->DestroyRecipientAgreement(address, tid);
732 }
733 }
735 {
736 // Setup ADDBA response timeout
738 p->PeekHeader(addBa);
739 Ptr<QosTxop> edca = m_mac->GetQosTxop(addBa.GetTid());
740 Simulator::Schedule(edca->GetAddBaResponseTimeout(),
742 edca,
743 address,
744 addBa.GetTid());
745 }
747 {
748 // A recipient Block Ack agreement must exist
750 p->PeekHeader(addBa);
751 auto tid = addBa.GetTid();
752 NS_ASSERT_MSG(GetBaManager(tid)->GetAgreementAsRecipient(address, tid),
753 "Recipient BA agreement {" << address << ", " << +tid
754 << "} not found");
755 m_pendingAddBaResp.erase({address, tid});
756 }
757 }
758 }
760}
761
762void
764{
765 NS_LOG_DEBUG(this);
766
769 {
770 // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
771 // be transmitted (as responses) the following within the current TXOP:
772 // f) Any number of BlockAckReq frames
773 // (Sec. 10.22.2.8 of 802.11-2016)
774 NS_LOG_DEBUG("Schedule a transmission from Block Ack Manager in a SIFS");
777
778 // TXOP limit is null, hence the txopDuration parameter is unused
780
782 {
784 }
785 m_sentFrameTo.clear();
786 }
787 else
788 {
790 }
791}
792
793void
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);
822 Simulator::Schedule(qosTxop->GetFailedAddBaTimeout(),
824 qosTxop,
825 recipient,
826 tid);
827 }
828 }
829 }
830 // the MPDU may have been dropped (and dequeued) by the above call to the NotifyDiscardedMpdu
831 // method of the BlockAckManager with reason WIFI_MAC_DROP_QOS_OLD_PACKET; in such a case, we
832 // must not fire the dropped callback again (with reason WIFI_MAC_DROP_REACHED_RETRY_LIMIT)
833 if (mpdu->IsQueued())
834 {
836 }
837}
838
839void
841{
842 NS_LOG_FUNCTION(this << *mpdu);
843
844 if (mpdu->GetHeader().IsQosData())
845 {
846 uint8_t tid = mpdu->GetHeader().GetQosTid();
847 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
848
849 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
850 {
851 // notify the BA manager that the MPDU was not acknowledged
852 edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
853 return;
854 }
855 }
857}
858
859void
861{
862 NS_LOG_FUNCTION(this << *psdu);
863
864 auto tids = psdu->GetTids();
865
866 if (tids.empty() || // no QoS data frames included
867 !m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin()))
868 {
870 return;
871 }
872
873 // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
874 auto mpduIt = psdu->end();
875
876 do
877 {
878 std::advance(mpduIt, -1);
879
880 const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
881 if (hdr.IsQosData())
882 {
883 uint8_t tid = hdr.GetQosTid();
884 NS_ASSERT(m_mac->GetBaAgreementEstablishedAsOriginator(hdr.GetAddr1(), tid));
885
886 if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
887 {
888 // The MPDU has never been transmitted, so we can make its sequence
889 // number available again if it is the highest sequence number
890 // assigned by the MAC TX middle
891 uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
892
893 if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
894 {
895 (*mpduIt)->UnassignSeqNo();
896 m_txMiddle->SetSequenceNumberFor(&hdr);
897
898 NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
899 << ", next sequence "
900 "number for dest="
901 << hdr.GetAddr1() << ",tid=" << +tid << " is "
902 << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
903 }
904 }
905 }
906 } while (mpduIt != psdu->begin());
907}
908
909Time
911{
912 NS_LOG_FUNCTION(this << txDuration << &txParams);
913
915
917 {
918 NS_ASSERT(txParams.m_acknowledgment &&
919 txParams.m_acknowledgment->acknowledgmentTime.has_value());
920 return *txParams.m_acknowledgment->acknowledgmentTime;
921 }
922
923 // under multiple protection settings, if the TXOP limit is not null, Duration/ID
924 // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
925 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
926 // of 802.11-2016)
927 return std::max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
928}
929
930void
932{
933 NS_LOG_FUNCTION(this << psdu << &txParams);
934
935 m_psdu = psdu;
936 m_txParams = std::move(txParams);
937
938#ifdef NS3_BUILD_PROFILE_DEBUG
939 // If protection is required, the MPDUs must be stored in some queue because
940 // they are not put back in a queue if the RTS/CTS exchange fails
942 {
943 for (const auto& mpdu : *PeekPointer(m_psdu))
944 {
945 NS_ASSERT(mpdu->GetHeader().IsCtl() || mpdu->IsQueued());
946 }
947 }
948#endif
949
950 // Make sure that the acknowledgment time has been computed, so that SendRts()
951 // and SendCtsToSelf() can reuse this value.
953
954 if (!m_txParams.m_acknowledgment->acknowledgmentTime.has_value())
955 {
957 }
958
959 // Set QoS Ack policy
961
962 for (const auto& mpdu : *PeekPointer(m_psdu))
963 {
964 if (mpdu->IsQueued())
965 {
966 mpdu->SetInFlight(m_linkId);
967 }
968 }
969
971}
972
973void
975{
976 NS_LOG_FUNCTION(this);
977 if (m_psdu)
978 {
980 m_sentRtsTo.clear();
982 {
983 SendPsdu();
984 }
985 else
986 {
988 }
989 return;
990 }
992}
993
994void
996{
997 NS_LOG_FUNCTION(this << *rts << txVector);
998
999 if (!m_psdu)
1000 {
1001 // A CTS Timeout occurred when protecting a single MPDU is handled by the
1002 // parent classes
1004 return;
1005 }
1006
1008 m_psdu = nullptr;
1009}
1010
1011void
1013{
1014 NS_LOG_FUNCTION(this);
1015
1016 Time txDuration =
1018
1020
1022 {
1024
1025 std::set<uint8_t> tids = m_psdu->GetTids();
1026 NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
1027
1028 if (tids.empty() || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
1029 {
1030 // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
1032 }
1033 }
1035 {
1037
1038 // the timeout duration is "aSIFSTime + aSlotTime + aRxPHYStartDelay, starting
1039 // at the PHY-TXEND.confirm primitive" (section 10.3.2.9 or 10.22.2.2 of 802.11-2016).
1040 // aRxPHYStartDelay equals the time to transmit the PHY header.
1041 auto blockAcknowledgment = static_cast<WifiBlockAck*>(m_txParams.m_acknowledgment.get());
1042
1043 Time timeout =
1044 txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
1045 m_phy->CalculatePhyPreambleAndHeaderDuration(blockAcknowledgment->blockAckTxVector);
1048 timeout,
1049 {m_psdu->GetAddr1()},
1051 this,
1052 m_psdu,
1054 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
1055 }
1057 {
1059
1060 // schedule the transmission of a BAR in a SIFS
1061 std::set<uint8_t> tids = m_psdu->GetTids();
1062 NS_ABORT_MSG_IF(tids.size() > 1,
1063 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
1064 uint8_t tid = *tids.begin();
1065
1066 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1067 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid);
1068 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1069
1071 }
1072 else
1073 {
1074 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
1075 << m_txParams.m_acknowledgment.get() << ")");
1076 }
1077
1078 // transmit the PSDU
1079 if (m_psdu->GetNMpdus() > 1)
1080 {
1082 }
1083 else
1084 {
1086 }
1087
1088 if (m_txTimer.IsRunning())
1089 {
1090 NS_ASSERT(m_sentFrameTo.empty());
1092 }
1093
1095 {
1096 // we are done in case the A-MPDU does not require acknowledgment
1097 m_psdu = nullptr;
1098 }
1099}
1100
1101void
1103{
1104 NS_LOG_FUNCTION(this << psdu);
1105
1106 for (const auto& mpdu : *PeekPointer(psdu))
1107 {
1108 auto& hdr = mpdu->GetHeader();
1109
1110 if (hdr.IsQosData() && hdr.HasData())
1111 {
1112 auto tid = hdr.GetQosTid();
1113 m_mac->GetQosTxop(tid)->CompleteMpduTx(mpdu);
1114 }
1115 }
1116}
1117
1118void
1120{
1121 NS_LOG_FUNCTION(this << psdu);
1122
1123 // use an array to avoid computing the queue size for every MPDU in the PSDU
1124 std::array<std::optional<uint8_t>, 8> queueSizeForTid;
1125
1126 for (const auto& mpdu : *PeekPointer(psdu))
1127 {
1128 WifiMacHeader& hdr = mpdu->GetHeader();
1129
1130 if (hdr.IsQosData())
1131 {
1132 uint8_t tid = hdr.GetQosTid();
1133 auto edca = m_mac->GetQosTxop(tid);
1134
1135 if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
1136 {
1137 // set the Queue Size subfield of the QoS Control field
1138 if (!queueSizeForTid[tid].has_value())
1139 {
1140 queueSizeForTid[tid] =
1141 edca->GetQosQueueSize(tid, mpdu->GetOriginal()->GetHeader().GetAddr1());
1142 }
1143
1144 hdr.SetQosEosp();
1145 hdr.SetQosQueueSize(queueSizeForTid[tid].value());
1146 }
1147 }
1148 }
1149
1151}
1152
1153void
1155{
1156 NS_LOG_FUNCTION(this << *psdu);
1157 for (const auto& mpdu : *PeekPointer(psdu))
1158 {
1159 DequeueMpdu(mpdu);
1160 }
1161}
1162
1163void
1165{
1166 NS_LOG_FUNCTION(this << psdu << txVector);
1167
1168 NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
1169 FinalizeMacHeader(psdu);
1170 NotifyTxToEdca(psdu);
1171 m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
1172
1173 if (psdu->IsAggregate())
1174 {
1175 txVector.SetAggregation(true);
1176 }
1177
1178 auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
1179 // The TXNAV timer is a single timer, shared by the EDCAFs within a STA, that is initialized
1180 // with the duration from the Duration/ID field in the frame most recently successfully
1181 // transmitted by the TXOP holder, except for PS-Poll frames. (Sec.10.23.2.2 IEEE 802.11-2020)
1182 if (!psdu->GetHeader(0).IsPsPoll())
1183 {
1184 m_txNav = Max(m_txNav, Simulator::Now() + txDuration + psdu->GetDuration());
1185 }
1186
1187 m_phy->Send(psdu, txVector);
1188}
1189
1190bool
1192 const WifiTxParameters& txParams,
1193 Time ppduDurationLimit) const
1194{
1195 NS_ASSERT(mpdu);
1196 NS_LOG_FUNCTION(this << *mpdu << &txParams << ppduDurationLimit);
1197
1198 Mac48Address receiver = mpdu->GetHeader().GetAddr1();
1199 uint32_t ampduSize = txParams.GetSize(receiver);
1200
1201 if (!txParams.LastAddedIsFirstMpdu(receiver))
1202 {
1203 // we are attempting to perform A-MPDU aggregation, hence we have to check
1204 // that we meet the limit on the max A-MPDU size
1205 uint8_t tid;
1206 const WifiTxParameters::PsduInfo* info;
1207
1208 if (mpdu->GetHeader().IsQosData())
1209 {
1210 tid = mpdu->GetHeader().GetQosTid();
1211 }
1212 else if ((info = txParams.GetPsduInfo(receiver)) && !info->seqNumbers.empty())
1213 {
1214 tid = info->seqNumbers.begin()->first;
1215 }
1216 else
1217 {
1218 NS_ABORT_MSG("Cannot aggregate a non-QoS data frame to an A-MPDU that does"
1219 " not contain any QoS data frame");
1220 }
1221
1222 WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1223
1224 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1225 {
1226 return false;
1227 }
1228 }
1229
1230 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1231}
1232
1233bool
1235 Mac48Address receiver,
1236 uint8_t tid,
1237 WifiModulationClass modulation) const
1238{
1239 NS_LOG_FUNCTION(this << ampduSize << receiver << +tid << modulation);
1240
1241 uint32_t maxAmpduSize = m_mpduAggregator->GetMaxAmpduSize(receiver, tid, modulation);
1242
1243 if (maxAmpduSize == 0)
1244 {
1245 NS_LOG_DEBUG("A-MPDU aggregation disabled");
1246 return false;
1247 }
1248
1249 if (ampduSize > maxAmpduSize)
1250 {
1251 NS_LOG_DEBUG("the frame does not meet the constraint on max A-MPDU size (" << maxAmpduSize
1252 << ")");
1253 return false;
1254 }
1255 return true;
1256}
1257
1258bool
1260 WifiTxParameters& txParams,
1261 Time availableTime) const
1262{
1263 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1264 NS_LOG_FUNCTION(this << *msdu << &txParams << availableTime);
1265
1266 // tentatively aggregate the given MPDU
1267 auto prevTxDuration = txParams.m_txDuration;
1268 txParams.AggregateMsdu(msdu);
1269 UpdateTxDuration(msdu->GetHeader().GetAddr1(), txParams);
1270
1271 // check if aggregating the given MSDU requires a different protection method
1272 NS_ASSERT(txParams.m_protection);
1273 auto protectionTime = txParams.m_protection->protectionTime;
1274
1275 std::unique_ptr<WifiProtection> protection;
1276 protection = GetProtectionManager()->TryAggregateMsdu(msdu, txParams);
1277 bool protectionSwapped = false;
1278
1279 if (protection)
1280 {
1281 // the protection method has changed, calculate the new protection time
1282 CalculateProtectionTime(protection.get());
1283 protectionTime = protection->protectionTime;
1284 // swap unique pointers, so that the txParams that is passed to the next
1285 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1286 txParams.m_protection.swap(protection);
1287 protectionSwapped = true;
1288 }
1289 NS_ASSERT(protectionTime.has_value());
1290
1291 // check if aggregating the given MSDU requires a different acknowledgment method
1292 NS_ASSERT(txParams.m_acknowledgment);
1293 auto acknowledgmentTime = txParams.m_acknowledgment->acknowledgmentTime;
1294
1295 std::unique_ptr<WifiAcknowledgment> acknowledgment;
1296 acknowledgment = GetAckManager()->TryAggregateMsdu(msdu, txParams);
1297 bool acknowledgmentSwapped = false;
1298
1299 if (acknowledgment)
1300 {
1301 // the acknowledgment method has changed, calculate the new acknowledgment time
1302 CalculateAcknowledgmentTime(acknowledgment.get());
1303 acknowledgmentTime = acknowledgment->acknowledgmentTime;
1304 // swap unique pointers, so that the txParams that is passed to the next
1305 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1306 txParams.m_acknowledgment.swap(acknowledgment);
1307 acknowledgmentSwapped = true;
1308 }
1309 NS_ASSERT(acknowledgmentTime.has_value());
1310
1311 Time ppduDurationLimit = Time::Min();
1312 if (availableTime != Time::Min())
1313 {
1314 ppduDurationLimit = availableTime - *protectionTime - *acknowledgmentTime;
1315 }
1316
1317 if (!IsWithinLimitsIfAggregateMsdu(msdu, txParams, ppduDurationLimit))
1318 {
1319 // adding MPDU failed, undo the addition of the MPDU and restore protection and
1320 // acknowledgment methods if they were swapped
1321 txParams.UndoAddMpdu();
1322 txParams.m_txDuration = prevTxDuration;
1323 if (protectionSwapped)
1324 {
1325 txParams.m_protection.swap(protection);
1326 }
1327 if (acknowledgmentSwapped)
1328 {
1329 txParams.m_acknowledgment.swap(acknowledgment);
1330 }
1331 return false;
1332 }
1333
1334 return true;
1335}
1336
1337bool
1339 const WifiTxParameters& txParams,
1340 Time ppduDurationLimit) const
1341{
1342 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1343 NS_LOG_FUNCTION(this << *msdu << &txParams << ppduDurationLimit);
1344
1345 auto receiver = msdu->GetHeader().GetAddr1();
1346 auto tid = msdu->GetHeader().GetQosTid();
1347 auto modulation = txParams.m_txVector.GetModulationClass();
1348 auto psduInfo = txParams.GetPsduInfo(receiver);
1349 NS_ASSERT_MSG(psduInfo, "No PSDU info for receiver " << receiver);
1350
1351 // Check that the limit on A-MSDU size is met
1352 uint16_t maxAmsduSize = m_msduAggregator->GetMaxAmsduSize(receiver, tid, modulation);
1353
1354 if (maxAmsduSize == 0)
1355 {
1356 NS_LOG_DEBUG("A-MSDU aggregation disabled");
1357 return false;
1358 }
1359
1360 if (psduInfo->amsduSize > maxAmsduSize)
1361 {
1362 NS_LOG_DEBUG("No other MSDU can be aggregated: maximum A-MSDU size (" << maxAmsduSize
1363 << ") reached ");
1364 return false;
1365 }
1366
1367 const WifiTxParameters::PsduInfo* info = txParams.GetPsduInfo(msdu->GetHeader().GetAddr1());
1368 NS_ASSERT(info);
1369 auto ampduSize = txParams.GetSize(receiver);
1370
1371 if (info->ampduSize > 0)
1372 {
1373 // the A-MSDU being built is aggregated to other MPDUs in an A-MPDU.
1374 // Check that the limit on A-MPDU size is met.
1375 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1376 {
1377 return false;
1378 }
1379 }
1380
1381 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1382}
1383
1384void
1386{
1387 NS_LOG_FUNCTION(this << *psdu << txVector);
1388
1389 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1390
1391 MissedBlockAck(psdu, txVector);
1392
1393 m_psdu = nullptr;
1395}
1396
1397void
1399{
1400 NS_LOG_FUNCTION(this << psdu << txVector);
1401
1402 auto recipient = psdu->GetAddr1();
1403 auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
1404 bool isBar;
1405 uint8_t tid;
1406
1407 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
1408 {
1409 isBar = true;
1410 CtrlBAckRequestHeader baReqHdr;
1411 psdu->GetPayload(0)->PeekHeader(baReqHdr);
1412 tid = baReqHdr.GetTidInfo();
1413 }
1414 else
1415 {
1416 isBar = false;
1417 std::set<uint8_t> tids = psdu->GetTids();
1418 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
1419 NS_ASSERT(!tids.empty());
1420 tid = *tids.begin();
1421
1423 ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
1424
1425 if (auto droppedMpdu = DropMpduIfRetryLimitReached(psdu))
1426 {
1427 // notify remote station manager if at least an MPDU was dropped
1428 GetWifiRemoteStationManager()->ReportFinalDataFailed(droppedMpdu);
1429 }
1430 }
1431
1432 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1433
1434 if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
1435 {
1436 // we have to send a BlockAckReq, if needed
1437 if (GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld))
1438 {
1439 NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
1440 /**
1441 * The BlockAckReq must be sent on the same link as the data frames to avoid issues.
1442 * As an example, assume that an A-MPDU is sent on link 0, the BlockAck timer
1443 * expires and the BlockAckReq is sent on another link (e.g., on link 1). When the
1444 * originator processes the BlockAck response, it will not interpret a '0' in the
1445 * bitmap corresponding to the transmitted MPDUs as a negative acknowledgment,
1446 * because the BlockAck is received on a different link than the one on which the
1447 * MPDUs are (still) inflight. Hence, such MPDUs stay inflight and are not
1448 * retransmitted.
1449 */
1450 if (isBar)
1451 {
1452 psdu->GetHeader(0).SetRetry();
1453 }
1454 else
1455 {
1456 // missed block ack after data frame with Implicit BAR Ack policy
1457 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid);
1458 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1459 }
1460 }
1461 else
1462 {
1463 NS_LOG_DEBUG("Missed Block Ack, do not transmit a BlockAckReq");
1464 // if a BA agreement exists, we can get here if there is no outstanding
1465 // MPDU whose lifetime has not expired yet.
1466 if (isBar)
1467 {
1468 DequeuePsdu(psdu);
1469 }
1470 if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
1471 {
1472 // schedule a BlockAckRequest to be sent only if there are data frames queued
1473 // for this recipient
1474 GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
1475 }
1476 }
1477 }
1478 else
1479 {
1480 // we have to retransmit the data frames, if needed
1481 GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
1482 }
1483}
1484
1485void
1487 Time durationId,
1488 WifiTxVector& blockAckTxVector,
1489 double rxSnr)
1490{
1491 NS_LOG_FUNCTION(this << durationId << blockAckTxVector << rxSnr);
1492
1493 WifiMacHeader hdr;
1495 auto addr1 = agreement.GetPeer();
1496 if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
1497 {
1498 addr1 = *originator;
1499 }
1500 hdr.SetAddr1(addr1);
1501 hdr.SetAddr2(m_self);
1502 hdr.SetDsNotFrom();
1503 hdr.SetDsNotTo();
1504
1505 CtrlBAckResponseHeader blockAck;
1506 blockAck.SetType(agreement.GetBlockAckType());
1507 blockAck.SetTidInfo(agreement.GetTid());
1508 agreement.FillBlockAckBitmap(blockAck);
1509
1510 Ptr<Packet> packet = Create<Packet>();
1511 packet->AddHeader(blockAck);
1512 Ptr<WifiPsdu> psdu = GetWifiPsdu(Create<WifiMpdu>(packet, hdr), blockAckTxVector);
1513
1514 // 802.11-2016, Section 9.2.5.7: In a BlockAck frame transmitted in response
1515 // to a BlockAckReq frame or transmitted in response to a frame containing an
1516 // implicit block ack request, the Duration/ID field is set to the value obtained
1517 // from the Duration/ ID field of the frame that elicited the response minus the
1518 // time, in microseconds between the end of the PPDU carrying the frame that
1519 // elicited the response and the end of the PPDU carrying the BlockAck frame.
1520 Time baDurationId = durationId - m_phy->GetSifs() -
1521 WifiPhy::CalculateTxDuration(psdu, blockAckTxVector, m_phy->GetPhyBand());
1522 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016)
1523 if (baDurationId.IsStrictlyNegative())
1524 {
1525 baDurationId = Seconds(0);
1526 }
1527 psdu->GetHeader(0).SetDuration(baDurationId);
1528
1529 SnrTag tag;
1530 tag.Set(rxSnr);
1531 psdu->GetPayload(0)->AddPacketTag(tag);
1532
1533 ForwardPsduDown(psdu, blockAckTxVector);
1534}
1535
1536void
1538 RxSignalInfo rxSignalInfo,
1539 const WifiTxVector& txVector,
1540 bool inAmpdu)
1541{
1542 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
1543
1544 // The received MPDU is either broadcast or addressed to this station
1545 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1546
1547 double rxSnr = rxSignalInfo.snr;
1548 const WifiMacHeader& hdr = mpdu->GetHeader();
1549
1550 if (hdr.IsCtl())
1551 {
1552 if (hdr.IsCts() && m_txTimer.IsRunning() &&
1554 {
1555 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1556 NS_ASSERT(hdr.GetAddr1() == m_self);
1557
1558 Mac48Address sender = m_psdu->GetAddr1();
1559 NS_LOG_DEBUG("Received CTS from=" << sender);
1560
1561 SnrTag tag;
1562 mpdu->GetPacket()->PeekPacketTag(tag);
1563 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1564 GetWifiRemoteStationManager()->ReportRtsOk(m_psdu->GetHeader(0),
1565 rxSnr,
1566 txVector.GetMode(),
1567 tag.Get());
1568
1569 m_txTimer.Cancel();
1570 m_channelAccessManager->NotifyCtsTimeoutResetNow();
1572 }
1573 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1575 {
1576 Mac48Address sender = hdr.GetAddr2();
1577 NS_LOG_DEBUG("Received BlockAck from=" << sender);
1578 m_txTimer.GotResponseFrom(sender);
1579
1580 SnrTag tag;
1581 mpdu->GetPacket()->PeekPacketTag(tag);
1582
1583 // notify the Block Ack Manager
1584 CtrlBAckResponseHeader blockAck;
1585 mpdu->GetPacket()->PeekHeader(blockAck);
1586 uint8_t tid = blockAck.GetTidInfo();
1587 std::pair<uint16_t, uint16_t> ret =
1588 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
1589 blockAck,
1590 m_mac->GetMldAddress(sender).value_or(sender),
1591 {tid});
1592 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1593 ret.first,
1594 ret.second,
1595 rxSnr,
1596 tag.Get(),
1598
1599 // cancel the timer
1600 m_txTimer.Cancel();
1601 m_channelAccessManager->NotifyAckTimeoutResetNow();
1602
1603 // Reset the CW
1605
1606 // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
1607 if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
1608 {
1610 }
1611 m_psdu = nullptr;
1613 }
1614 else if (hdr.IsBlockAckReq())
1615 {
1616 NS_ASSERT(hdr.GetAddr1() == m_self);
1617 NS_ABORT_MSG_IF(inAmpdu, "BlockAckReq in A-MPDU is not supported");
1618
1619 Mac48Address sender = hdr.GetAddr2();
1620 NS_LOG_DEBUG("Received BlockAckReq from=" << sender);
1621
1622 CtrlBAckRequestHeader blockAckReq;
1623 mpdu->GetPacket()->PeekHeader(blockAckReq);
1624 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1625 uint8_t tid = blockAckReq.GetTidInfo();
1626
1627 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(sender, tid);
1628
1629 if (!agreement)
1630 {
1631 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1632 return;
1633 }
1634
1635 GetBaManager(tid)->NotifyGotBlockAckRequest(
1636 m_mac->GetMldAddress(sender).value_or(sender),
1637 tid,
1638 blockAckReq.GetStartingSequence());
1639
1640 NS_LOG_DEBUG("Schedule Block Ack");
1642 m_phy->GetSifs(),
1644 this,
1645 *agreement,
1646 hdr.GetDuration(),
1647 GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
1648 rxSnr);
1649 }
1650 else
1651 {
1652 // the received control frame cannot be handled here
1653 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1654 }
1655 return;
1656 }
1657
1658 if (hdr.IsQosData() && hdr.HasData() && hdr.GetAddr1() == m_self)
1659 {
1660 uint8_t tid = hdr.GetQosTid();
1661
1662 if (m_mac->GetBaAgreementEstablishedAsRecipient(hdr.GetAddr2(), tid))
1663 {
1664 // a Block Ack agreement has been established
1665 NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
1666
1667 GetBaManager(tid)->NotifyGotMpdu(mpdu);
1668
1669 if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
1670 {
1671 NS_LOG_DEBUG("Schedule Normal Ack");
1674 this,
1675 hdr,
1676 txVector,
1677 rxSnr);
1678 }
1679 return;
1680 }
1681 // We let the QosFrameExchangeManager handle QoS data frame not belonging
1682 // to a Block Ack agreement
1683 }
1684
1685 if (hdr.IsMgt() && hdr.IsAction())
1686 {
1687 ReceiveMgtAction(mpdu, txVector);
1688 }
1689
1690 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1691}
1692
1693void
1695{
1696 NS_LOG_FUNCTION(this << *mpdu << txVector);
1697
1698 NS_ASSERT(mpdu->GetHeader().IsAction());
1699 const auto from = mpdu->GetOriginal()->GetHeader().GetAddr2();
1700
1701 WifiActionHeader actionHdr;
1702 auto packet = mpdu->GetPacket()->Copy();
1703 packet->RemoveHeader(actionHdr);
1704
1705 // compute the time to transmit the Ack
1706 const auto ackTxVector =
1707 GetWifiRemoteStationManager()->GetAckTxVector(mpdu->GetHeader().GetAddr2(), txVector);
1708 const auto ackTxTime =
1710
1711 switch (actionHdr.GetCategory())
1712 {
1714
1715 switch (actionHdr.GetAction().blockAck)
1716 {
1718 MgtAddBaRequestHeader reqHdr;
1719 packet->RemoveHeader(reqHdr);
1720
1721 // We've received an ADDBA Request. Our policy here is to automatically accept it,
1722 // so we get the ADDBA Response on its way as soon as we finish transmitting the Ack,
1723 // to avoid to concurrently send Ack and ADDBA Response in case of multi-link devices
1724 Simulator::Schedule(m_phy->GetSifs() + ackTxTime,
1726 this,
1727 reqHdr,
1728 from);
1729 // This frame is now completely dealt with, so we're done.
1730 return;
1731 }
1733 MgtAddBaResponseHeader respHdr;
1734 packet->RemoveHeader(respHdr);
1735
1736 // We've received an ADDBA Response. Wait until we finish transmitting the Ack before
1737 // unblocking transmissions to the recipient, otherwise for multi-link devices the Ack
1738 // may be sent concurrently with a data frame containing an A-MPDU
1739 Simulator::Schedule(m_phy->GetSifs() + ackTxTime, [=, this]() {
1740 const auto recipient =
1741 GetWifiRemoteStationManager()->GetMldAddress(from).value_or(from);
1742 m_mac->GetQosTxop(respHdr.GetTid())->GotAddBaResponse(respHdr, recipient);
1743 GetBaManager(respHdr.GetTid())
1744 ->SetBlockAckInactivityCallback(
1745 MakeCallback(&HtFrameExchangeManager::SendDelbaFrame, this));
1746 });
1747 // This frame is now completely dealt with, so we're done.
1748 return;
1749 }
1751 MgtDelBaHeader delBaHdr;
1752 packet->RemoveHeader(delBaHdr);
1753 auto recipient = GetWifiRemoteStationManager()->GetMldAddress(from).value_or(from);
1754
1755 if (delBaHdr.IsByOriginator())
1756 {
1757 // This DELBA frame was sent by the originator, so
1758 // this means that an ingoing established
1759 // agreement exists in BlockAckManager and we need to
1760 // destroy it.
1761 GetBaManager(delBaHdr.GetTid())
1762 ->DestroyRecipientAgreement(recipient, delBaHdr.GetTid());
1763 }
1764 else
1765 {
1766 // We must have been the originator. We need to
1767 // tell the correct queue that the agreement has
1768 // been torn down
1769 m_mac->GetQosTxop(delBaHdr.GetTid())->GotDelBaFrame(&delBaHdr, recipient);
1770 }
1771 // This frame is now completely dealt with, so we're done.
1772 return;
1773 }
1774 default:
1775 NS_FATAL_ERROR("Unsupported Action field in Block Ack Action frame");
1776 }
1777 default:
1778 // Other action frames are not processed here
1779 ;
1780 }
1781}
1782
1783void
1785 const RxSignalInfo& rxSignalInfo,
1786 const WifiTxVector& txVector,
1787 const std::vector<bool>& perMpduStatus)
1788{
1790 this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
1791 << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](bool v) { return v; }));
1792
1793 std::set<uint8_t> tids = psdu->GetTids();
1794
1795 // Multi-TID A-MPDUs are not supported yet
1796 if (tids.size() == 1)
1797 {
1798 uint8_t tid = *tids.begin();
1799 WifiMacHeader::QosAckPolicy ackPolicy = psdu->GetAckPolicyForTid(tid);
1800 NS_ASSERT(psdu->GetNMpdus() > 1);
1801
1802 if (ackPolicy == WifiMacHeader::NORMAL_ACK)
1803 {
1804 // Normal Ack or Implicit Block Ack Request
1805 NS_LOG_DEBUG("Schedule Block Ack");
1806 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
1807 NS_ASSERT(agreement);
1808
1810 m_phy->GetSifs(),
1812 this,
1813 *agreement,
1814 psdu->GetDuration(),
1815 GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
1816 rxSignalInfo.snr);
1817 }
1818 }
1819}
1820
1821} // namespace ns3
#define Max(a, b)
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.
Headers for BlockAckRequest.
uint16_t GetStartingSequence() const
Return the starting sequence number.
uint8_t GetTidInfo() const
Return the Traffic ID (TID).
void SetStartingSequence(uint16_t seq)
Set the starting sequence number from the given raw sequence control field.
Headers for BlockAck response.
uint8_t GetTidInfo(std::size_t index=0) const
For Block Ack variants other than Multi-STA Block Ack, get the TID_INFO subfield of the BA Control fi...
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
bool m_protectedIfResponded
whether a STA is assumed to be protected if replied to a frame requiring acknowledgment
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.
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 immediately, if no protection was used, or in a SIFS, if protection was compl...
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
Ptr< WifiMpdu > DropMpduIfRetryLimitReached(Ptr< WifiPsdu > psdu)
Wrapper for the GetMpdusToDropOnTxFailure function of the remote station manager that additionally dr...
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...
std::set< Mac48Address > m_sentFrameTo
the STA(s) to which we sent a frame requesting a response
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
virtual bool StartTransmission(Ptr< Txop > dcf, MHz_u allowedWidth)
Request the FrameExchangeManager to start a frame exchange sequence.
MHz_u m_allowedWidth
the allowed width for the current transmission
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.
bool SendAddBaRequest(Mac48Address recipient, uint8_t tid, uint16_t startingSeq, uint16_t timeout, bool immediateBAck, Time availableTime, std::optional< Mac48Address > gcrGroupAddr=std::nullopt)
Sends an ADDBA Request to establish a block ack agreement with STA addressed by recipient for TID tid...
void ProtectionCompleted() override
Transmit prepared frame immediately, if no protection was used, or in a SIFS, if protection was compl...
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...
void ReceiveMgtAction(Ptr< const WifiMpdu > mpdu, const WifiTxVector &txVector)
Process a received management action frame that relates to 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...
virtual void MissedBlockAck(Ptr< WifiPsdu > psdu, const WifiTxVector &txVector)
Take necessary actions when a BlockAck is missed, such as scheduling a BlockAckReq frame or the retra...
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...
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
bool IsGroup() const
Implement the header for management frames of type Add Block Ack request.
std::optional< Mac48Address > GetGcrGroupAddress() const
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.
void SetGcrGroupAddress(const Mac48Address &address)
Set the GCR Group address.
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 SetGcrGroupAddress(const Mac48Address &address)
Set the GCR Group address.
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.
Smart pointer class similar to boost::intrusive_ptr.
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)
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
void TransmissionFailed(bool forceCurrentCw=false) override
Take necessary actions upon a transmission failure.
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.
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition qos-txop.cc:802
void AddBaResponseTimeout(Mac48Address recipient, uint8_t tid)
Callback when ADDBA response is not received after timeout.
Definition qos-txop.cc:742
virtual Time GetRemainingTxop(uint8_t linkId) const
Return the remaining duration in the current TXOP on the given link.
Definition qos-txop.cc:637
void ResetBa(Mac48Address recipient, uint8_t tid)
Reset BA agreement after BA negotiation failed.
Definition qos-txop.cc:755
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:560
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
Introspection did not find any typical Config paths.
Definition snr-tag.h:24
void Set(double snr)
Set the SNR to the given value.
Definition snr-tag.cc:73
double Get() const
Return the SNR value.
Definition snr-tag.cc:79
Status code for association response.
Definition status-code.h:21
void SetSuccess()
Set success bit to 0 (success).
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition nstime.h:276
bool IsStrictlyNegative() const
Exactly equivalent to t < 0.
Definition nstime.h:331
bool IsZero() const
Exactly equivalent to t == 0.
Definition nstime.h:304
Time GetTxopLimit() const
Return the TXOP limit.
Definition txop.cc:602
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:368
a unique identifier for an interface.
Definition type-id.h:48
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:1001
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.
bool IsRetry() const
Return if the Retry bit is set.
bool IsMgt() const
Return true if the Type is Management.
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 IsAction() const
Return true if the header is an Action header.
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 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.
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:1807
Time GetSlot() const
Return the slot duration for this PHY.
Definition wifi-phy.cc:841
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition wifi-phy.cc:829
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1587
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1069
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1580
std::set< uint8_t > GetTids() const
Get the set of TIDs of the QoS Data frames included in the PSDU.
Definition wifi-psdu.cc:177
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition wifi-psdu.cc:278
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition wifi-psdu.cc:332
uint32_t GetSize() const
Return the size of the PSDU in bytes.
Definition wifi-psdu.cc:272
Mac48Address GetAddr1() const
Get the Receiver Address (RA), which is common to all the MPDUs.
Definition wifi-psdu.cc:102
void SetDuration(Time duration)
Set the Duration/ID field on all the MPDUs.
Definition wifi-psdu.cc:157
std::size_t GetNMpdus() const
Return the number of MPDUs constituting the PSDU.
Definition wifi-psdu.cc:326
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:191
This class stores the TX parameters (TX vector, protection mechanism, acknowledgment mechanism,...
std::optional< Time > m_txDuration
TX duration of the frame.
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,...
void UndoAddMpdu()
Undo the addition of the last MPDU added by calling AddMpdu().
bool LastAddedIsFirstMpdu(Mac48Address receiver) const
Check if the last added MPDU is the first MPDU for the given receiver.
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.
void GotResponseFrom(const Mac48Address &from)
Notify that a response was got from the given station.
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.
MHz_u 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:55
#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:75
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition abort.h:97
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition abort.h:65
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#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 ",...
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:619
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition object-base.h:35
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:436
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1368
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
AcIndex QosUtilsMapTidToAc(uint8_t tid)
Maps TID (Traffic ID) to Access classes.
Definition qos-utils.cc:123
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:165
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:62
@ STA
Definition wifi-mac.h:58
@ WIFI_MOD_CLASS_HT
HT (Clause 19)
Every class exported by the ns3 library is enclosed in the ns3 namespace.
U * PeekPointer(const Ptr< U > &p)
Definition ptr.h:443
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:68
@ 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:243
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition wifi-utils.cc:58
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition wifi-utils.cc:50
ns3::Time timeout
RxSignalInfo structure containing info on the received signal.
Definition wifi-types.h:72
double snr
SNR in linear scale.
Definition wifi-types.h:73
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