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/ap-wifi-mac.h"
15#include "ns3/assert.h"
16#include "ns3/ctrl-headers.h"
17#include "ns3/gcr-manager.h"
18#include "ns3/log.h"
19#include "ns3/mgt-action-headers.h"
20#include "ns3/recipient-block-ack-agreement.h"
21#include "ns3/snr-tag.h"
22#include "ns3/sta-wifi-mac.h"
23#include "ns3/vht-configuration.h"
24#include "ns3/wifi-mac-queue.h"
25#include "ns3/wifi-net-device.h"
26#include "ns3/wifi-utils.h"
27
28#include <array>
29#include <optional>
30
31#undef NS_LOG_APPEND_CONTEXT
32#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
33
34namespace ns3
35{
36
37NS_LOG_COMPONENT_DEFINE("HtFrameExchangeManager");
38
39NS_OBJECT_ENSURE_REGISTERED(HtFrameExchangeManager);
40
41TypeId
43{
44 static TypeId tid = TypeId("ns3::HtFrameExchangeManager")
46 .AddConstructor<HtFrameExchangeManager>()
47 .SetGroupName("Wifi");
48 return tid;
49}
50
57
62
63void
65{
66 NS_LOG_FUNCTION(this);
67 if (m_flushGroupcastMpdusEvent.IsPending())
68 {
70 }
71 m_pendingAddBaResp.clear();
72 m_msduAggregator = nullptr;
73 m_mpduAggregator = nullptr;
74 m_psdu = nullptr;
75 m_txParams.Clear();
77}
78
79void
86
92
98
101{
102 return m_mac->GetQosTxop(tid)->GetBaManager();
103}
104
105bool
107{
108 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
109 bool establish;
110
111 // NOLINTBEGIN(bugprone-branch-clone)
112 if (!m_mac->GetHtConfiguration() ||
113 (!GetWifiRemoteStationManager()->GetHtSupported(recipient) &&
114 !GetWifiRemoteStationManager()->GetStationHe6GhzCapabilities(recipient)))
115 {
116 // no Block Ack if this device or the recipient are not HT STAs and do not operate
117 // in the 6 GHz band
118 establish = false;
119 }
120 else if (auto agreement = qosTxop->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
121 agreement && !agreement->get().IsReset())
122 {
123 // Block Ack agreement already established
124 establish = false;
125 }
126 // NOLINTEND(bugprone-branch-clone)
127 else
128 {
130 uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
131 establish =
132 (m_mac->Is6GhzBand(m_linkId) ||
133 (qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
134 (m_mpduAggregator->GetMaxAmpduSize(recipient, tid, WIFI_MOD_CLASS_HT) > 0 &&
135 packets > 1) ||
136 m_mac->GetVhtConfiguration());
137 }
138
139 NS_LOG_FUNCTION(this << recipient << +tid << establish);
140 return establish;
141}
142
143std::optional<Mac48Address>
145{
146 NS_ASSERT(m_mac->GetTypeOfStation() == AP && m_apMac->UseGcr(header));
147 const auto& groupAddress = header.GetAddr1();
148
149 const auto tid = header.GetQosTid();
150 auto qosTxop = m_mac->GetQosTxop(tid);
151 const auto maxMpduSize =
152 m_mpduAggregator->GetMaxAmpduSize(groupAddress, tid, WIFI_MOD_CLASS_HT);
153 const auto isGcrBa = (m_apMac->GetGcrManager()->GetRetransmissionPolicy() ==
156
157 for (const auto& recipients =
158 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(groupAddress);
159 const auto& nextRecipient : recipients)
160 {
161 if (auto agreement =
162 qosTxop->GetBaManager()->GetAgreementAsOriginator(nextRecipient, tid, groupAddress);
163 agreement && !agreement->get().IsReset())
164 {
165 continue;
166 }
167
168 const auto packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
169 const auto establish =
170 (isGcrBa ||
171 (qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
172 (maxMpduSize > 0 && packets > 1));
173 NS_LOG_FUNCTION(this << groupAddress << +tid << establish);
174 if (establish)
175 {
176 return nextRecipient;
177 }
178 }
179
180 return std::nullopt;
181}
182
183bool
185 uint8_t tid,
186 uint16_t startingSeq,
187 uint16_t timeout,
188 bool immediateBAck,
189 Time availableTime,
190 std::optional<Mac48Address> gcrGroupAddr)
191{
192 NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck << availableTime
193 << gcrGroupAddr.has_value());
194 NS_LOG_DEBUG("Send ADDBA request to " << dest);
195
196 WifiMacHeader hdr;
198 // use the remote link address if dest is an MLD address
199 auto addr1 = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(dest);
200 hdr.SetAddr1(addr1 ? *addr1 : dest);
201 hdr.SetAddr2(m_self);
202 hdr.SetAddr3(m_bssid);
203 hdr.SetDsNotTo();
204 hdr.SetDsNotFrom();
205
206 WifiActionHeader actionHdr;
209 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
210
211 Ptr<Packet> packet = Create<Packet>();
212 // Setting ADDBARequest header
214 reqHdr.SetAmsduSupport(true);
215 if (immediateBAck)
216 {
217 reqHdr.SetImmediateBlockAck();
218 }
219 else
220 {
221 reqHdr.SetDelayedBlockAck();
222 }
223 reqHdr.SetTid(tid);
224 /* For now we don't use buffer size field in the ADDBA request frame. The recipient
225 * will choose how many packets it can receive under block ack.
226 */
227 reqHdr.SetBufferSize(0);
228 reqHdr.SetTimeout(timeout);
229 // set the starting sequence number for the BA agreement
230 reqHdr.SetStartingSequence(startingSeq);
231
232 if (gcrGroupAddr)
233 {
234 reqHdr.SetGcrGroupAddress(*gcrGroupAddr);
235 }
236
237 GetBaManager(tid)->CreateOriginatorAgreement(reqHdr, dest);
238
239 packet->AddHeader(reqHdr);
240 packet->AddHeader(actionHdr);
241
242 Ptr<WifiMpdu> mpdu = Create<WifiMpdu>(packet, hdr);
243
244 // get the sequence number for the ADDBA Request management frame
245 uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
246 mpdu->GetHeader().SetSequenceNumber(sequence);
247
248 WifiTxParameters txParams;
249 txParams.m_txVector =
250 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
251 if (!TryAddMpdu(mpdu, txParams, availableTime))
252 {
253 NS_LOG_DEBUG("Not enough time to send the ADDBA Request frame");
254 return false;
255 }
256
257 // Wifi MAC queue scheduler is expected to prioritize management frames
258 m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(mpdu);
259 SendMpduWithProtection(mpdu, txParams);
260 return true;
261}
262
263void
265 Mac48Address originator)
266{
267 NS_LOG_FUNCTION(this << originator);
268 WifiMacHeader hdr;
270 hdr.SetAddr1(originator);
271 hdr.SetAddr2(m_self);
272 hdr.SetAddr3(m_bssid);
273 hdr.SetDsNotFrom();
274 hdr.SetDsNotTo();
275
277 StatusCode code;
278 code.SetSuccess();
279 respHdr.SetStatusCode(code);
280 // Here a control about queues type?
281 respHdr.SetAmsduSupport(reqHdr.IsAmsduSupported());
282
283 if (reqHdr.IsImmediateBlockAck())
284 {
285 respHdr.SetImmediateBlockAck();
286 }
287 else
288 {
289 respHdr.SetDelayedBlockAck();
290 }
291 auto tid = reqHdr.GetTid();
292 respHdr.SetTid(tid);
293
294 auto bufferSize = std::min(m_mac->GetMpduBufferSize(), m_mac->GetMaxBaBufferSize(originator));
295 respHdr.SetBufferSize(bufferSize);
296 respHdr.SetTimeout(reqHdr.GetTimeout());
297
298 if (auto gcrGroupAddr = reqHdr.GetGcrGroupAddress())
299 {
300 respHdr.SetGcrGroupAddress(*gcrGroupAddr);
301 }
302
303 WifiActionHeader actionHdr;
306 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
307
308 Ptr<Packet> packet = Create<Packet>();
309 packet->AddHeader(respHdr);
310 packet->AddHeader(actionHdr);
311
312 // Get the MLD address of the originator, if an ML setup was performed
313 if (auto originatorMld = GetWifiRemoteStationManager()->GetMldAddress(originator))
314 {
315 originator = *originatorMld;
316 }
317 GetBaManager(tid)->CreateRecipientAgreement(respHdr,
318 originator,
319 reqHdr.GetStartingSequence(),
320 m_rxMiddle);
321
322 auto agreement =
323 GetBaManager(tid)->GetAgreementAsRecipient(originator, tid, reqHdr.GetGcrGroupAddress());
324 NS_ASSERT(agreement);
325 if (respHdr.GetTimeout() != 0)
326 {
327 Time timeout = MicroSeconds(1024 * agreement->get().GetTimeout());
328
329 agreement->get().m_inactivityEvent =
332 this,
333 originator,
334 tid,
335 false,
336 reqHdr.GetGcrGroupAddress());
337 }
338
339 auto mpdu = Create<WifiMpdu>(packet, hdr);
340
341 /*
342 * It is possible (though, unlikely) that at this point there are other ADDBA_RESPONSE frame(s)
343 * in the MAC queue. This may happen if the recipient receives an ADDBA_REQUEST frame, enqueues
344 * an ADDBA_RESPONSE frame, but is not able to successfully transmit it before the timer to
345 * wait for ADDBA_RESPONSE expires at the originator. The latter may then send another
346 * ADDBA_REQUEST frame, which triggers the creation of another ADDBA_RESPONSE frame.
347 * To avoid sending unnecessary ADDBA_RESPONSE frames, we keep track of the previously enqueued
348 * ADDBA_RESPONSE frame (if any), dequeue it and replace it with the new ADDBA_RESPONSE frame.
349 */
350
351 // remove any pending ADDBA_RESPONSE frame
352 AgreementKey key(originator, tid);
353 if (auto it = m_pendingAddBaResp.find(key); it != m_pendingAddBaResp.end())
354 {
355 NS_ASSERT_MSG(it->second, "The pointer to the pending ADDBA_RESPONSE cannot be null");
356 DequeueMpdu(it->second);
357 m_pendingAddBaResp.erase(it);
358 }
359 // store the new ADDBA_RESPONSE frame
360 m_pendingAddBaResp[key] = mpdu;
361
362 // It is unclear which queue this frame should go into. For now we
363 // bung it into the queue corresponding to the TID for which we are
364 // establishing an agreement, and push it to the head.
365 // Wifi MAC queue scheduler is expected to prioritize management frames
366 m_mac->GetQosTxop(tid)->Queue(mpdu);
367}
368
369void
371 uint8_t tid,
372 bool byOriginator,
373 std::optional<Mac48Address> gcrGroupAddr)
374{
375 NS_LOG_FUNCTION(this << addr << +tid << byOriginator << gcrGroupAddr.has_value());
376 WifiMacHeader hdr;
378 // use the remote link address if addr is an MLD address
379 hdr.SetAddr1(GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr).value_or(addr));
380 hdr.SetAddr2(m_self);
381 hdr.SetAddr3(m_bssid);
382 hdr.SetDsNotTo();
383 hdr.SetDsNotFrom();
384
385 MgtDelBaHeader delbaHdr;
386 delbaHdr.SetTid(tid);
387 byOriginator ? delbaHdr.SetByOriginator() : delbaHdr.SetByRecipient();
388 if (gcrGroupAddr.has_value())
389 {
390 delbaHdr.SetGcrGroupAddress(gcrGroupAddr.value());
391 }
392
393 WifiActionHeader actionHdr;
395 action.blockAck = WifiActionHeader::BLOCK_ACK_DELBA;
396 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
397
398 Ptr<Packet> packet = Create<Packet>();
399 packet->AddHeader(delbaHdr);
400 packet->AddHeader(actionHdr);
401
402 m_mac->GetQosTxop(tid)->Queue(Create<WifiMpdu>(packet, hdr));
403}
404
405uint16_t
407{
408 // if the peeked MPDU has been already transmitted, use its sequence number
409 // as the starting sequence number for the BA agreement, otherwise use the
410 // next available sequence number
411 return header.IsRetry()
412 ? header.GetSequenceNumber()
413 : m_txMiddle->GetNextSeqNumberByTidAndAddress(header.GetQosTid(), header.GetAddr1());
414}
415
416bool
418{
419 NS_ASSERT_MSG(GetWifiRemoteStationManager()->IsInPsMode(sender),
420 sender << " is not in powersave mode");
421
422 auto senderMld = GetWifiRemoteStationManager()->GetMldAddress(sender).value_or(sender);
423
424 for (auto aciIt = wifiAcList.crbegin(); aciIt != wifiAcList.crend(); ++aciIt)
425 {
426 // unblock queues storing control frames, otherwise GetBar() will not return a BlockAckReq
427 if (GetWifiRemoteStationManager()->GetMldAddress(sender))
428 {
429 // the sender is an MLD, unblock queues storing control frames that use MLD addresses
430 m_mac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::POWER_SAVE_MODE,
431 aciIt->first,
432 {WIFI_CTL_QUEUE},
433 senderMld,
434 m_mac->GetLocalAddress(senderMld),
435 {},
436 {m_linkId});
437 }
438 // unblock queues storing control frames that use link addresses
439 m_mac->GetMacQueueScheduler()->UnblockQueues(WifiQueueBlockedReason::POWER_SAVE_MODE,
440 aciIt->first,
441 {WIFI_CTL_QUEUE},
442 sender,
443 GetAddress(),
444 {},
445 {m_linkId});
446
447 auto mpdu = GetBar(aciIt->first, aciIt->second.GetHighTid(), senderMld);
448 if (!mpdu)
449 {
450 mpdu = GetBar(aciIt->first, aciIt->second.GetLowTid(), senderMld);
451 }
452
453 // block queues storing control frames
454 if (GetWifiRemoteStationManager()->GetMldAddress(sender))
455 {
456 m_mac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::POWER_SAVE_MODE,
457 aciIt->first,
458 {WIFI_CTL_QUEUE},
459 senderMld,
460 m_mac->GetLocalAddress(senderMld),
461 {},
462 {m_linkId});
463 }
464 m_mac->GetMacQueueScheduler()->BlockQueues(WifiQueueBlockedReason::POWER_SAVE_MODE,
465 aciIt->first,
466 {WIFI_CTL_QUEUE},
467 sender,
468 GetAddress(),
469 {},
470 {m_linkId});
471
472 if (mpdu && SendMpduFromBaManager(mpdu, Time::Min(), false))
473 {
474 return true;
475 }
476 }
477
479}
480
481bool
482HtFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
483{
484 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
485
486 // First, check if there is a BAR to be transmitted
487 if (auto mpdu = GetBar(edca->GetAccessCategory());
488 mpdu && SendMpduFromBaManager(mpdu, availableTime, initialFrame))
489 {
490 return true;
491 }
492
493 Ptr<WifiMpdu> peekedItem = edca->PeekNextMpdu(m_linkId);
494
495 // Even though channel access is requested when the queue is not empty, at
496 // the time channel access is granted the lifetime of the packet might be
497 // expired and the queue might be empty.
498 if (!peekedItem)
499 {
500 NS_LOG_DEBUG("No frames available for transmission");
501 return false;
502 }
503
504 const WifiMacHeader& hdr = peekedItem->GetHeader();
505 // setup a Block Ack agreement if needed
506 if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() &&
508 {
509 return SendAddBaRequest(hdr.GetAddr1(),
510 hdr.GetQosTid(),
512 edca->GetBlockAckInactivityTimeout(),
513 true,
514 availableTime);
515 }
516 else if (IsGcr(m_mac, hdr))
517 {
518 if (const auto addbaRecipient = NeedSetupGcrBlockAck(hdr))
519 {
520 return SendAddBaRequest(addbaRecipient.value(),
521 hdr.GetQosTid(),
523 edca->GetBlockAckInactivityTimeout(),
524 true,
525 availableTime,
526 hdr.GetAddr1());
527 }
528 }
529
530 // Use SendDataFrame if we can try aggregation
531 if (hdr.IsQosData() && !hdr.GetAddr1().IsBroadcast() && !peekedItem->IsFragment() &&
532 !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem =
533 CreateAliasIfNeeded(peekedItem)))
534 {
535 return SendDataFrame(peekedItem, availableTime, initialFrame);
536 }
537
538 // Use the QoS FEM to transmit the frame in all the other cases, i.e.:
539 // - the frame is not a QoS data frame
540 // - the frame is a broadcast QoS data frame
541 // - the frame is a fragment
542 // - the frame must be fragmented
543 return QosFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
544}
545
548 std::optional<uint8_t> optTid,
549 std::optional<Mac48Address> optAddress)
550{
551 NS_LOG_FUNCTION(this << +ac << optTid.has_value() << optAddress.has_value());
552 NS_ASSERT_MSG(optTid.has_value() == optAddress.has_value(),
553 "Either both or none of TID and address must be provided");
554
555 // remove all expired MPDUs from the MAC queue, so that
556 // BlockAckRequest frames (if needed) are scheduled
557 auto queue = m_mac->GetTxopQueue(ac);
558 queue->WipeAllExpiredMpdus();
559
560 Ptr<WifiMpdu> bar;
561 Ptr<WifiMpdu> prevBar;
562 Ptr<WifiMpdu> selectedBar;
563
564 // we could iterate over all the scheduler's queues and ignore those that do not contain
565 // control frames, but it's more efficient to peek frames until we get frames that are
566 // not control frames, given that control frames have the highest priority
567 while ((bar = queue->PeekFirstAvailable(m_linkId, prevBar)) && bar && bar->GetHeader().IsCtl())
568 {
569 if (bar->GetHeader().IsBlockAckReq())
570 {
572 bar->GetPacket()->PeekHeader(reqHdr);
573 auto tid = reqHdr.GetTidInfo();
574 Mac48Address recipient = bar->GetHeader().GetAddr1();
575 auto recipientMld = m_mac->GetMldAddress(recipient);
576
577 // the scheduler should not return a BlockAckReq that cannot be sent on this link:
578 // either the TA address is the address of this link or it is the MLD address and
579 // the RA field is the MLD address of a device we can communicate with on this link
580 NS_ASSERT_MSG(bar->GetHeader().GetAddr2() == m_self ||
581 (bar->GetHeader().GetAddr2() == m_mac->GetAddress() && recipientMld &&
582 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)),
583 "Cannot use link " << +m_linkId << " to send BAR: " << *bar);
584
585 if (optAddress &&
586 (GetWifiRemoteStationManager()->GetMldAddress(*optAddress).value_or(*optAddress) !=
587 GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient) ||
588 optTid != tid))
589 {
590 NS_LOG_DEBUG("BAR " << *bar
591 << " cannot be returned because it is not addressed"
592 " to the given station for the given TID");
593 prevBar = bar;
594 continue;
595 }
596
597 auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(
598 recipient,
599 tid,
600 reqHdr.IsGcr() ? std::optional{reqHdr.GetGcrGroupAddress()} : std::nullopt);
601 if (const auto isGcrBa =
602 reqHdr.IsGcr() && (m_apMac->GetGcrManager()->GetRetransmissionPolicy() ==
604 agreement && reqHdr.IsGcr() && !isGcrBa)
605 {
606 NS_LOG_DEBUG("Skip GCR BAR if GCR-BA retransmission policy is not selected");
607 queue->Remove(bar);
608 continue;
609 }
610 else if (!agreement)
611 {
612 NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
613 << " was torn down");
614 queue->Remove(bar);
615 continue;
616 }
617 // update BAR if the starting sequence number changed
618 if (auto seqNo = agreement->get().GetStartingSequence();
619 reqHdr.GetStartingSequence() != seqNo)
620 {
621 reqHdr.SetStartingSequence(seqNo);
622 Ptr<Packet> packet = Create<Packet>();
623 packet->AddHeader(reqHdr);
624 auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader(), bar->GetTimestamp());
625 queue->Replace(bar, updatedBar);
626 bar = updatedBar;
627 }
628 // bar is the BlockAckReq to send
629 selectedBar = bar;
630
631 // if the selected BAR is intended to be sent on this specific link and the recipient
632 // is an MLD, remove the BAR (if any) for this BA agreement that can be sent on any
633 // link (because a BAR that can be sent on any link to a recipient is no longer
634 // needed after sending a BAR to that recipient on this link)
635 if (bar->GetHeader().GetAddr2() == m_self && recipientMld)
636 {
639 *recipientMld,
640 std::nullopt};
641 Ptr<WifiMpdu> otherBar;
642 while ((otherBar = queue->PeekByQueueId(queueId, otherBar)))
643 {
644 if (otherBar->GetHeader().IsBlockAckReq())
645 {
646 CtrlBAckRequestHeader otherReqHdr;
647 otherBar->GetPacket()->PeekHeader(otherReqHdr);
648 if (otherReqHdr.GetTidInfo() == tid)
649 {
650 queue->Remove(otherBar);
651 break;
652 }
653 }
654 }
655 }
656 break;
657 }
658 if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
659 {
660 return bar;
661 }
662 // not a BAR nor a Trigger Frame, continue
663 prevBar = bar;
664 }
665
666 if (!selectedBar)
667 {
668 // check if we can send a BAR to a recipient to which a BAR can only be sent if data queued
669 auto baManager = m_mac->GetQosTxop(ac)->GetBaManager();
670 for (const auto& [recipient, tid] : baManager->GetSendBarIfDataQueuedList())
671 {
672 WifiContainerQueueId queueId(
675 GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient),
676 tid);
677 // check if data is queued and can be transmitted on this link
678 if (queue->PeekByTidAndAddress(tid, recipient) &&
679 !m_mac->GetTxBlockedOnLink(QosUtilsMapTidToAc(tid), queueId, m_linkId))
680 {
681 auto [reqHdr, hdr] = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
682 auto pkt = Create<Packet>();
683 pkt->AddHeader(reqHdr);
684 selectedBar = Create<WifiMpdu>(pkt, hdr);
685 baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
686 queue->Enqueue(selectedBar);
687 break;
688 }
689 }
690 }
691
692 if (selectedBar)
693 {
694 if (const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
695 currAddr1 == m_mac->GetMldAddress(currAddr1))
696 {
697 // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
698 // and move to the appropriate container queue
699 DequeueMpdu(selectedBar);
700 const auto addr1 =
701 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(
702 currAddr1);
703 selectedBar->GetHeader().SetAddr1(addr1);
704 selectedBar->GetHeader().SetAddr2(m_self);
705 queue->Enqueue(selectedBar);
706 }
707 }
708
709 return selectedBar;
710}
711
712bool
714 Time availableTime,
715 bool initialFrame)
716{
717 NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
718
719 // First, check if there is a BAR to be transmitted
720 if (!mpdu->GetHeader().IsBlockAckReq())
721 {
722 NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
723 return false;
724 }
725
726 // Prepare the TX parameters. Note that the default ack manager expects the
727 // data TxVector in the m_txVector field to compute the BlockAck TxVector.
728 // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
729 // a few lines below.
730 WifiTxParameters txParams;
731 txParams.m_txVector =
732 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
733
734 if (!TryAddMpdu(mpdu, txParams, availableTime))
735 {
736 NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
737 return false;
738 }
739
741
742 // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
743 auto blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
744 txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
745
746 // we can transmit the BlockAckReq frame
747 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
748 return true;
749}
750
751bool
753 Time availableTime,
754 bool initialFrame)
755{
756 NS_ASSERT(peekedItem && peekedItem->GetHeader().IsQosData() &&
757 !peekedItem->GetHeader().GetAddr1().IsBroadcast() && !peekedItem->IsFragment());
758 NS_LOG_FUNCTION(this << *peekedItem << availableTime << initialFrame);
759
760 Ptr<QosTxop> edca = m_mac->GetQosTxop(peekedItem->GetHeader().GetQosTid());
761 WifiTxParameters txParams;
762 txParams.m_txVector =
763 GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
764 Ptr<WifiMpdu> mpdu =
765 edca->GetNextMpdu(m_linkId, peekedItem, txParams, availableTime, initialFrame);
766
767 if (!mpdu)
768 {
769 NS_LOG_DEBUG("Not enough time to transmit a frame");
770 return false;
771 }
772
773 // try A-MPDU aggregation
774 std::vector<Ptr<WifiMpdu>> mpduList =
775 m_mpduAggregator->GetNextAmpdu(mpdu, txParams, availableTime);
776 NS_ASSERT(txParams.m_acknowledgment);
777
778 if (mpduList.size() > 1)
779 {
780 // A-MPDU aggregation succeeded
781 SendPsduWithProtection(Create<WifiPsdu>(std::move(mpduList)), txParams);
782 }
783 else if (txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
784 {
785 // a QoS data frame using the Block Ack policy can be followed by a BlockAckReq
786 // frame and a BlockAck frame. Such a sequence is handled by the HT FEM
787 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
788 }
789 else
790 {
791 // transmission can be handled by the base FEM
792 SendMpduWithProtection(mpdu, txParams);
793 }
794
795 return true;
796}
797
798void
800{
801 NS_LOG_FUNCTION(this << acknowledgment);
802 NS_ASSERT(acknowledgment);
803
804 if (acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
805 {
806 auto blockAcknowledgment = static_cast<WifiBlockAck*>(acknowledgment);
807 auto baTxDuration =
808 WifiPhy::CalculateTxDuration(GetBlockAckSize(blockAcknowledgment->baType),
809 blockAcknowledgment->blockAckTxVector,
810 m_phy->GetPhyBand());
811 blockAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + baTxDuration;
812 }
813 else if (acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
814 {
815 auto barBlockAcknowledgment = static_cast<WifiBarBlockAck*>(acknowledgment);
816 auto barTxDuration =
817 WifiPhy::CalculateTxDuration(GetBlockAckRequestSize(barBlockAcknowledgment->barType),
818 barBlockAcknowledgment->blockAckReqTxVector,
819 m_phy->GetPhyBand());
820 auto baTxDuration =
821 WifiPhy::CalculateTxDuration(GetBlockAckSize(barBlockAcknowledgment->baType),
822 barBlockAcknowledgment->blockAckTxVector,
823 m_phy->GetPhyBand());
824 barBlockAcknowledgment->acknowledgmentTime =
825 2 * m_phy->GetSifs() + barTxDuration + baTxDuration;
826 }
827 else
828 {
830 }
831}
832
833void
835{
836 ForwardPsduDown(GetWifiPsdu(mpdu, txVector), txVector);
837}
838
841{
842 return Create<WifiPsdu>(mpdu, false);
843}
844
845void
847{
848 NS_LOG_FUNCTION(this << *mpdu);
849
850 if (mpdu->GetHeader().IsQosData())
851 {
852 uint8_t tid = mpdu->GetHeader().GetQosTid();
853 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
854
855 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
856 {
857 // notify the BA manager that the MPDU was acknowledged
858 edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
859 // the BA manager fires the AckedMpdu trace source, so nothing else must be done
860 return;
861 }
862 }
863 else if (mpdu->GetHeader().IsAction())
864 {
865 auto addr1 = mpdu->GetHeader().GetAddr1();
866 auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
867 WifiActionHeader actionHdr;
868 Ptr<Packet> p = mpdu->GetPacket()->Copy();
869 p->RemoveHeader(actionHdr);
870 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
871 {
873 {
874 MgtDelBaHeader delBa;
875 p->PeekHeader(delBa);
876 auto tid = delBa.GetTid();
877 if (delBa.IsByOriginator())
878 {
879 GetBaManager(tid)->DestroyOriginatorAgreement(address,
880 tid,
881 delBa.GetGcrGroupAddress());
882 }
883 else
884 {
885 GetBaManager(tid)->DestroyRecipientAgreement(address,
886 tid,
887 delBa.GetGcrGroupAddress());
888 }
889 }
891 {
892 // Setup ADDBA response timeout
894 p->PeekHeader(addBa);
895 Ptr<QosTxop> edca = m_mac->GetQosTxop(addBa.GetTid());
896 Simulator::Schedule(edca->GetAddBaResponseTimeout(),
898 edca,
899 address,
900 addBa.GetTid(),
901 addBa.GetGcrGroupAddress());
902 }
904 {
905 // A recipient Block Ack agreement must exist
907 p->PeekHeader(addBa);
908 auto tid = addBa.GetTid();
910 GetBaManager(tid)->GetAgreementAsRecipient(address,
911 tid,
912 addBa.GetGcrGroupAddress()),
913 "Recipient BA agreement {" << address << ", " << +tid << "} not found");
914 m_pendingAddBaResp.erase({address, tid});
915 }
916 }
917 }
919}
920
921void
923{
924 NS_LOG_DEBUG(this);
925
926 if (m_edca && m_edca->GetTxopLimit(m_linkId).IsZero() && GetBar(m_edca->GetAccessCategory()) &&
927 (m_txNav > Simulator::Now() + m_phy->GetSifs()))
928 {
929 // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
930 // be transmitted (as responses) the following within the current TXOP:
931 // f) Any number of BlockAckReq frames
932 // (Sec. 10.22.2.8 of 802.11-2016)
933 NS_LOG_DEBUG("Schedule a transmission from Block Ack Manager in a SIFS");
936
937 // TXOP limit is null, hence the txopDuration parameter is unused
938 Simulator::Schedule(m_phy->GetSifs(), fp, this, m_edca, Seconds(0));
939
941 {
943 }
944 m_sentFrameTo.clear();
945 }
946 else
947 {
949 }
950}
951
952void
954{
955 NS_LOG_FUNCTION(this << *mpdu);
956
957 if (mpdu->GetHeader().IsQosData())
958 {
959 GetBaManager(mpdu->GetHeader().GetQosTid())->NotifyDiscardedMpdu(mpdu);
960 }
961 else if (mpdu->GetHeader().IsAction())
962 {
963 WifiActionHeader actionHdr;
964 mpdu->GetPacket()->PeekHeader(actionHdr);
965 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
967 {
968 const auto tid = GetTid(mpdu->GetPacket(), mpdu->GetHeader());
969 auto recipient = mpdu->GetHeader().GetAddr1();
970 // if the recipient is an MLD, use its MLD address
971 if (auto mldAddr = GetWifiRemoteStationManager()->GetMldAddress(recipient))
972 {
973 recipient = *mldAddr;
974 }
975 auto p = mpdu->GetPacket()->Copy();
976 p->RemoveHeader(actionHdr);
978 p->PeekHeader(addBa);
979 if (auto agreement =
980 GetBaManager(tid)->GetAgreementAsOriginator(recipient,
981 tid,
982 addBa.GetGcrGroupAddress());
983 agreement && agreement->get().IsPending())
984 {
985 NS_LOG_DEBUG("No ACK after ADDBA request");
986 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
987 qosTxop->NotifyOriginatorAgreementNoReply(recipient,
988 tid,
989 addBa.GetGcrGroupAddress());
990 Simulator::Schedule(qosTxop->GetFailedAddBaTimeout(),
992 qosTxop,
993 recipient,
994 tid,
995 addBa.GetGcrGroupAddress());
996 }
997 }
998 }
999 // the MPDU may have been dropped (and dequeued) by the above call to the NotifyDiscardedMpdu
1000 // method of the BlockAckManager with reason WIFI_MAC_DROP_QOS_OLD_PACKET; in such a case, we
1001 // must not fire the dropped callback again (with reason WIFI_MAC_DROP_REACHED_RETRY_LIMIT)
1002 if (mpdu->IsQueued())
1003 {
1005 }
1006}
1007
1008void
1010{
1011 NS_LOG_FUNCTION(this << *mpdu);
1012
1013 if (mpdu->GetHeader().IsQosData())
1014 {
1015 uint8_t tid = mpdu->GetHeader().GetQosTid();
1016 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1017
1018 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
1019 {
1020 // notify the BA manager that the MPDU was not acknowledged
1021 edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
1022 return;
1023 }
1024 }
1026}
1027
1028void
1030{
1031 NS_LOG_FUNCTION(this << *psdu);
1032
1033 const auto tids = psdu->GetTids();
1034 const auto isGcr = IsGcr(m_mac, psdu->GetHeader(0));
1035 auto agreementEstablished =
1036 !tids.empty() /* no QoS data frame included */ &&
1037 (isGcr ? GetBaManager(*tids.begin())
1038 ->IsGcrAgreementEstablished(
1039 psdu->GetHeader(0).GetAddr1(),
1040 *tids.begin(),
1041 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(
1042 psdu->GetHeader(0).GetAddr1()))
1043 : m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin())
1044 .has_value());
1045
1046 if (!agreementEstablished)
1047 {
1049 return;
1050 }
1051
1052 // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
1053 auto mpduIt = psdu->end();
1054
1055 do
1056 {
1057 std::advance(mpduIt, -1);
1058
1059 const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
1060 if (hdr.IsQosData())
1061 {
1062 uint8_t tid = hdr.GetQosTid();
1063 agreementEstablished =
1064 isGcr ? GetBaManager(tid)->IsGcrAgreementEstablished(
1065 psdu->GetHeader(0).GetAddr1(),
1066 tid,
1067 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(
1068 psdu->GetHeader(0).GetAddr1()))
1069 : m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), tid)
1070 .has_value();
1071 NS_ASSERT(agreementEstablished);
1072
1073 if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
1074 {
1075 // The MPDU has never been transmitted, so we can make its sequence
1076 // number available again if it is the highest sequence number
1077 // assigned by the MAC TX middle
1078 uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
1079
1080 if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
1081 {
1082 (*mpduIt)->UnassignSeqNo();
1083 m_txMiddle->SetSequenceNumberFor(&hdr);
1084
1085 NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
1086 << ", next sequence "
1087 "number for dest="
1088 << hdr.GetAddr1() << ",tid=" << +tid << " is "
1089 << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
1090 }
1091 }
1092 }
1093 } while (mpduIt != psdu->begin());
1094}
1095
1096Time
1098{
1099 NS_LOG_FUNCTION(this << txDuration << &txParams);
1100
1101 NS_ASSERT(txParams.m_acknowledgment &&
1102 txParams.m_acknowledgment->acknowledgmentTime.has_value());
1103
1104 const auto singleDurationId = *txParams.m_acknowledgment->acknowledgmentTime;
1105
1106 // m_edca is null if we were given the right to transmit a frame (e.g., we received a PS-Poll
1107 // frame); in such a case, use the Duration/ID value for the single protection case
1108 if (!m_edca || m_edca->GetTxopLimit(m_linkId).IsZero())
1109 {
1110 return singleDurationId;
1111 }
1112
1113 // under multiple protection settings, if the TXOP limit is not null, Duration/ID
1114 // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
1115 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
1116 // of 802.11-2016)
1117 auto duration = std::max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
1118
1120 {
1121 duration = std::min(duration, singleDurationId + m_singleExchangeProtectionSurplus);
1122 }
1123
1124 return duration;
1125}
1126
1127void
1129{
1130 NS_LOG_FUNCTION(this << psdu << &txParams);
1131
1132 m_psdu = psdu;
1133 m_txParams = std::move(txParams);
1134
1135#ifdef NS3_BUILD_PROFILE_DEBUG
1136 // If protection is required, the MPDUs must be stored in some queue because
1137 // they are not put back in a queue if the RTS/CTS exchange fails
1138 if (m_txParams.m_protection->method != WifiProtection::NONE)
1139 {
1140 for (const auto& mpdu : *PeekPointer(m_psdu))
1141 {
1142 NS_ASSERT(mpdu->GetHeader().IsCtl() || mpdu->IsQueued());
1143 }
1144 }
1145#endif
1146
1147 // Make sure that the acknowledgment time has been computed, so that SendRts()
1148 // and SendCtsToSelf() can reuse this value.
1149 NS_ASSERT(m_txParams.m_acknowledgment);
1150
1151 if (!m_txParams.m_acknowledgment->acknowledgmentTime.has_value())
1152 {
1153 CalculateAcknowledgmentTime(m_txParams.m_acknowledgment.get());
1154 }
1155
1156 // Set QoS Ack policy
1157 WifiAckManager::SetQosAckPolicy(m_psdu, m_txParams.m_acknowledgment.get());
1158
1159 for (const auto& mpdu : *PeekPointer(m_psdu))
1160 {
1161 if (mpdu->IsQueued())
1162 {
1163 mpdu->SetInFlight(m_linkId);
1164 }
1165 }
1166
1168}
1169
1170void
1172{
1173 NS_LOG_FUNCTION(this);
1174 if (m_psdu)
1175 {
1177 m_sentRtsTo.clear();
1178 if (m_txParams.m_protection->method == WifiProtection::NONE)
1179 {
1180 SendPsdu();
1181 }
1182 else
1183 {
1185 }
1186 return;
1187 }
1189}
1190
1191void
1193{
1194 NS_LOG_FUNCTION(this << *rts << txVector);
1195
1196 if (!m_psdu)
1197 {
1198 // A CTS Timeout occurred when protecting a single MPDU is handled by the
1199 // parent classes
1201 return;
1202 }
1203
1205 m_psdu = nullptr;
1206}
1207
1208void
1210{
1211 NS_LOG_FUNCTION(this);
1212
1213 Time txDuration =
1214 WifiPhy::CalculateTxDuration(m_psdu->GetSize(), m_txParams.m_txVector, m_phy->GetPhyBand());
1215
1216 NS_ASSERT(m_txParams.m_acknowledgment);
1217
1218 if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
1219 {
1220 std::set<uint8_t> tids = m_psdu->GetTids();
1221 NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
1222
1223 if (m_mac->GetTypeOfStation() == AP && m_apMac->UseGcr(m_psdu->GetHeader(0)))
1224 {
1225 if (m_apMac->GetGcrManager()->KeepGroupcastQueued(*m_psdu->begin()))
1226 {
1227 // keep the groupcast frame in the queue for future retransmission
1228 Simulator::Schedule(txDuration + m_phy->GetSifs(), [=, this, psdu = m_psdu]() {
1229 NS_LOG_DEBUG("Prepare groupcast PSDU for retry");
1230 for (const auto& mpdu : *PeekPointer(psdu))
1231 {
1232 mpdu->ResetInFlight(m_linkId);
1233 // restore addr1 to the group address instead of the concealment address
1234 if (m_apMac->GetGcrManager()->UseConcealment(mpdu->GetHeader()))
1235 {
1236 mpdu->GetHeader().SetAddr1(mpdu->begin()->second.GetDestinationAddr());
1237 }
1238 mpdu->GetHeader().SetRetry();
1239 }
1240 });
1241 }
1242 else
1243 {
1244 if (m_apMac->GetGcrManager()->GetRetransmissionPolicy() ==
1246 {
1247 for (const auto& mpdu : *PeekPointer(m_psdu))
1248 {
1249 NotifyLastGcrUrTx(mpdu);
1250 }
1251 }
1253 }
1254 }
1255
1256 Simulator::Schedule(txDuration, [=, this]() {
1257 if ((!m_apMac || !m_apMac->UseGcr(m_psdu->GetHeader(0))) &&
1258 (tids.empty() ||
1259 m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK))
1260 {
1261 // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
1263 }
1265 m_psdu = nullptr;
1266 });
1267 }
1268 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
1269 {
1270 m_psdu->SetDuration(GetPsduDurationId(txDuration, m_txParams));
1271
1272 // the timeout duration is "aSIFSTime + aSlotTime + aRxPHYStartDelay, starting
1273 // at the PHY-TXEND.confirm primitive" (section 10.3.2.9 or 10.22.2.2 of 802.11-2016).
1274 // aRxPHYStartDelay equals the time to transmit the PHY header.
1275 auto blockAcknowledgment = static_cast<WifiBlockAck*>(m_txParams.m_acknowledgment.get());
1276
1277 Time timeout =
1278 txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
1279 WifiPhy::CalculatePhyPreambleAndHeaderDuration(blockAcknowledgment->blockAckTxVector);
1280 NS_ASSERT(!m_txTimer.IsRunning());
1281 m_txTimer.Set(WifiTxTimer::WAIT_BLOCK_ACK,
1282 timeout,
1283 {m_psdu->GetAddr1()},
1285 this,
1286 m_psdu,
1287 m_txParams.m_txVector);
1288 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
1289 }
1290 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
1291 {
1292 m_psdu->SetDuration(GetPsduDurationId(txDuration, m_txParams));
1293
1294 // schedule the transmission of a BAR in a SIFS
1295 const auto tids = m_psdu->GetTids();
1296 NS_ABORT_MSG_IF(tids.size() > 1,
1297 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
1298 const auto tid = *tids.begin();
1299
1300 auto edca = m_mac->GetQosTxop(tid);
1301 const auto isGcr = IsGcr(m_mac, m_psdu->GetHeader(0));
1302 const auto& recipients =
1303 isGcr ? m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(m_psdu->GetAddr1())
1304 : GcrManager::GcrMembers{m_psdu->GetAddr1()};
1305 std::optional<Mac48Address> gcrGroupAddress{isGcr ? std::optional{m_psdu->GetAddr1()}
1306 : std::nullopt};
1307 for (const auto& recipient : recipients)
1308 {
1309 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid, gcrGroupAddress);
1310 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1311 }
1312
1313 if (isGcr)
1314 {
1315 Simulator::Schedule(txDuration + m_phy->GetSifs(), [=, this, psdu = m_psdu]() {
1316 NS_LOG_DEBUG("Restore group address of PSDU");
1317 for (const auto& mpdu : *PeekPointer(psdu))
1318 {
1319 // restore addr1 to the group address instead of the concealment address
1320 if (m_apMac->GetGcrManager()->UseConcealment(mpdu->GetHeader()))
1321 {
1322 mpdu->GetHeader().SetAddr1(mpdu->begin()->second.GetDestinationAddr());
1323 }
1324 }
1325 });
1326 }
1327
1328 Simulator::Schedule(txDuration, [=, this]() {
1329 TransmissionSucceeded();
1330 m_psdu = nullptr;
1331 });
1332 }
1333 else
1334 {
1335 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
1336 << m_txParams.m_acknowledgment.get() << ")");
1337 }
1338
1339 // transmit the PSDU
1340 if (m_psdu->GetNMpdus() > 1)
1341 {
1342 ForwardPsduDown(m_psdu, m_txParams.m_txVector);
1343 }
1344 else
1345 {
1346 ForwardMpduDown(*m_psdu->begin(), m_txParams.m_txVector);
1347 }
1348
1349 if (m_txTimer.IsRunning())
1350 {
1351 NS_ASSERT(m_sentFrameTo.empty());
1352 m_sentFrameTo = {m_psdu->GetAddr1()};
1353 }
1354}
1355
1356void
1358{
1359 NS_LOG_FUNCTION(this << psdu);
1360
1361 for (const auto& mpdu : *PeekPointer(psdu))
1362 {
1363 auto& hdr = mpdu->GetHeader();
1364
1365 if (hdr.IsQosData() && hdr.HasData())
1366 {
1367 auto tid = hdr.GetQosTid();
1368 m_mac->GetQosTxop(tid)->CompleteMpduTx(mpdu);
1369 }
1370 }
1371}
1372
1373void
1375{
1376 NS_LOG_FUNCTION(this << psdu);
1377
1378 // use an array to avoid computing the queue size for every MPDU in the PSDU
1379 std::array<std::optional<uint8_t>, 8> queueSizeForTid;
1380
1381 for (const auto& mpdu : *PeekPointer(psdu))
1382 {
1383 WifiMacHeader& hdr = mpdu->GetHeader();
1384
1385 if (hdr.IsQosData())
1386 {
1387 uint8_t tid = hdr.GetQosTid();
1388 auto edca = m_mac->GetQosTxop(tid);
1389
1390 if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
1391 {
1392 // set the Queue Size subfield of the QoS Control field
1393 if (!queueSizeForTid[tid].has_value())
1394 {
1395 queueSizeForTid[tid] =
1396 edca->GetQosQueueSize(tid, mpdu->GetOriginal()->GetHeader().GetAddr1());
1397 }
1398
1399 hdr.SetQosEosp();
1400 hdr.SetQosQueueSize(queueSizeForTid[tid].value());
1401 }
1402
1403 if (m_mac->GetTypeOfStation() == AP && m_apMac->UseGcr(hdr) &&
1404 m_apMac->GetGcrManager()->UseConcealment(mpdu->GetHeader()))
1405 {
1406 const auto& gcrConcealmentAddress =
1407 m_apMac->GetGcrManager()->GetGcrConcealmentAddress();
1408 hdr.SetAddr1(gcrConcealmentAddress);
1409 }
1410 }
1411 }
1412
1414}
1415
1416void
1418{
1419 NS_LOG_FUNCTION(this << *psdu);
1420 for (const auto& mpdu : *PeekPointer(psdu))
1421 {
1422 DequeueMpdu(mpdu);
1423 }
1424}
1425
1426void
1428{
1429 NS_LOG_FUNCTION(this << psdu << txVector);
1430
1431 NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
1432 FinalizeMacHeader(psdu);
1433 NotifyTxToEdca(psdu);
1434 m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
1435
1436 if (psdu->IsAggregate())
1437 {
1438 txVector.SetAggregation(true);
1439 }
1440
1441 const auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
1442 SetTxNav(*psdu->begin(), txDuration);
1443
1444 const auto& hdr = psdu->GetHeader(0);
1445 // if this is an Ack or BlockAck sent to acknowledge a frame in response to a PS-Poll that we
1446 // sent, we need to take the actions required to conclude a frame exchange
1447 if (m_txTimer.IsRunning() && m_txTimer.GetReason() == WifiTxTimer::WAIT_DATA_AFTER_PS_POLL &&
1448 (hdr.IsAck() || hdr.IsBlockAck()) && hdr.GetAddr1() == m_bssid)
1449 {
1452 }
1453
1454 m_phy->Send(psdu, txVector);
1455}
1456
1457bool
1459 const WifiTxParameters& txParams,
1460 Time ppduDurationLimit) const
1461{
1462 NS_ASSERT(mpdu);
1463 NS_LOG_FUNCTION(this << *mpdu << &txParams << ppduDurationLimit);
1464
1465 Mac48Address receiver = mpdu->GetHeader().GetAddr1();
1466 uint32_t ampduSize = txParams.GetSize(receiver);
1467
1468 if (!txParams.LastAddedIsFirstMpdu(receiver))
1469 {
1470 // we are attempting to perform A-MPDU aggregation, hence we have to check
1471 // that we meet the limit on the max A-MPDU size
1472 uint8_t tid;
1473 const WifiTxParameters::PsduInfo* info;
1474
1475 if (mpdu->GetHeader().IsQosData())
1476 {
1477 tid = mpdu->GetHeader().GetQosTid();
1478 }
1479 else if ((info = txParams.GetPsduInfo(receiver)) && !info->seqNumbers.empty())
1480 {
1481 tid = info->seqNumbers.begin()->first;
1482 }
1483 else
1484 {
1485 NS_ABORT_MSG("Cannot aggregate a non-QoS data frame to an A-MPDU that does"
1486 " not contain any QoS data frame");
1487 }
1488
1489 WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1490
1491 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1492 {
1493 return false;
1494 }
1495 }
1496
1497 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1498}
1499
1500bool
1502 Mac48Address receiver,
1503 uint8_t tid,
1504 WifiModulationClass modulation) const
1505{
1506 NS_LOG_FUNCTION(this << ampduSize << receiver << +tid << modulation);
1507
1508 uint32_t maxAmpduSize = m_mpduAggregator->GetMaxAmpduSize(receiver, tid, modulation);
1509
1510 if (maxAmpduSize == 0)
1511 {
1512 NS_LOG_DEBUG("A-MPDU aggregation disabled");
1513 return false;
1514 }
1515
1516 if (ampduSize > maxAmpduSize)
1517 {
1518 NS_LOG_DEBUG("the frame does not meet the constraint on max A-MPDU size (" << maxAmpduSize
1519 << ")");
1520 return false;
1521 }
1522 return true;
1523}
1524
1525bool
1527 WifiTxParameters& txParams,
1528 Time availableTime) const
1529{
1530 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1531 NS_LOG_FUNCTION(this << *msdu << &txParams << availableTime);
1532
1533 // tentatively aggregate the given MPDU
1534 auto prevTxDuration = txParams.m_txDuration;
1535 txParams.AggregateMsdu(msdu);
1536 UpdateTxDuration(msdu->GetHeader().GetAddr1(), txParams);
1537
1538 // check if aggregating the given MSDU requires a different protection method
1539 NS_ASSERT(txParams.m_protection);
1540 auto protectionTime = txParams.m_protection->protectionTime;
1541
1542 std::unique_ptr<WifiProtection> protection;
1543 protection = GetProtectionManager()->TryAggregateMsdu(msdu, txParams);
1544 bool protectionSwapped = false;
1545
1546 if (protection)
1547 {
1548 // the protection method has changed, calculate the new protection time
1549 CalculateProtectionTime(protection.get());
1550 protectionTime = protection->protectionTime;
1551 // swap unique pointers, so that the txParams that is passed to the next
1552 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1553 txParams.m_protection.swap(protection);
1554 protectionSwapped = true;
1555 }
1556 NS_ASSERT(protectionTime.has_value());
1557
1558 // check if aggregating the given MSDU requires a different acknowledgment method
1559 NS_ASSERT(txParams.m_acknowledgment);
1560 auto acknowledgmentTime = txParams.m_acknowledgment->acknowledgmentTime;
1561
1562 std::unique_ptr<WifiAcknowledgment> acknowledgment;
1563 acknowledgment = GetAckManager()->TryAggregateMsdu(msdu, txParams);
1564 bool acknowledgmentSwapped = false;
1565
1566 if (acknowledgment)
1567 {
1568 // the acknowledgment method has changed, calculate the new acknowledgment time
1569 CalculateAcknowledgmentTime(acknowledgment.get());
1570 acknowledgmentTime = acknowledgment->acknowledgmentTime;
1571 // swap unique pointers, so that the txParams that is passed to the next
1572 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1573 txParams.m_acknowledgment.swap(acknowledgment);
1574 acknowledgmentSwapped = true;
1575 }
1576 NS_ASSERT(acknowledgmentTime.has_value());
1577
1578 Time ppduDurationLimit = Time::Min();
1579 if (availableTime != Time::Min())
1580 {
1581 ppduDurationLimit = availableTime - *protectionTime - *acknowledgmentTime;
1582 }
1583
1584 if (!IsWithinLimitsIfAggregateMsdu(msdu, txParams, ppduDurationLimit))
1585 {
1586 // adding MPDU failed, undo the addition of the MPDU and restore protection and
1587 // acknowledgment methods if they were swapped
1588 txParams.UndoAddMpdu();
1589 txParams.m_txDuration = prevTxDuration;
1590 if (protectionSwapped)
1591 {
1592 txParams.m_protection.swap(protection);
1593 }
1594 if (acknowledgmentSwapped)
1595 {
1596 txParams.m_acknowledgment.swap(acknowledgment);
1597 }
1598 return false;
1599 }
1600
1601 return true;
1602}
1603
1604bool
1606 const WifiTxParameters& txParams,
1607 Time ppduDurationLimit) const
1608{
1609 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1610 NS_LOG_FUNCTION(this << *msdu << &txParams << ppduDurationLimit);
1611
1612 auto receiver = msdu->GetHeader().GetAddr1();
1613 auto tid = msdu->GetHeader().GetQosTid();
1614 auto modulation = txParams.m_txVector.GetModulationClass();
1615 auto psduInfo = txParams.GetPsduInfo(receiver);
1616 NS_ASSERT_MSG(psduInfo, "No PSDU info for receiver " << receiver);
1617
1618 // Check that the limit on A-MSDU size is met
1619 uint16_t maxAmsduSize = m_msduAggregator->GetMaxAmsduSize(receiver, tid, modulation);
1620
1621 if (maxAmsduSize == 0)
1622 {
1623 NS_LOG_DEBUG("A-MSDU aggregation disabled");
1624 return false;
1625 }
1626
1627 if (psduInfo->amsduSize > maxAmsduSize)
1628 {
1629 NS_LOG_DEBUG("No other MSDU can be aggregated: maximum A-MSDU size (" << maxAmsduSize
1630 << ") reached ");
1631 return false;
1632 }
1633
1634 const WifiTxParameters::PsduInfo* info = txParams.GetPsduInfo(msdu->GetHeader().GetAddr1());
1635 NS_ASSERT(info);
1636 auto ampduSize = txParams.GetSize(receiver);
1637
1638 if (info->ampduSize > 0)
1639 {
1640 // the A-MSDU being built is aggregated to other MPDUs in an A-MPDU.
1641 // Check that the limit on A-MPDU size is met.
1642 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1643 {
1644 return false;
1645 }
1646 }
1647
1648 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1649}
1650
1651void
1653{
1654 NS_LOG_FUNCTION(this << *psdu << txVector);
1655
1656 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1657
1658 MissedBlockAck(psdu, txVector);
1659
1660 m_psdu = nullptr;
1661 if (m_edca)
1662 {
1664 }
1665 else
1666 {
1667 m_sentFrameTo.clear();
1668 }
1669}
1670
1671void
1673{
1674 NS_LOG_FUNCTION(this << psdu << txVector);
1675
1676 auto recipient = psdu->GetAddr1();
1677 auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
1678 bool isBar;
1679 uint8_t tid;
1680 std::optional<Mac48Address> gcrGroupAddress;
1681
1682 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
1683 {
1684 isBar = true;
1685 CtrlBAckRequestHeader baReqHdr;
1686 psdu->GetPayload(0)->PeekHeader(baReqHdr);
1687 tid = baReqHdr.GetTidInfo();
1688 if (baReqHdr.IsGcr())
1689 {
1690 gcrGroupAddress = baReqHdr.GetGcrGroupAddress();
1691 }
1692 }
1693 else
1694 {
1695 isBar = false;
1696 std::set<uint8_t> tids = psdu->GetTids();
1697 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
1698 NS_ASSERT(!tids.empty());
1699 tid = *tids.begin();
1700
1702 ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
1703
1704 if (auto droppedMpdu = DropMpduIfRetryLimitReached(psdu))
1705 {
1706 // notify remote station manager if at least an MPDU was dropped
1707 GetWifiRemoteStationManager()->ReportFinalDataFailed(droppedMpdu);
1708 }
1709 }
1710
1711 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1712
1713 if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
1714 {
1715 // we have to send a BlockAckReq, if needed
1716 const auto retransmitBar =
1717 gcrGroupAddress.has_value()
1718 ? GetBaManager(tid)->NeedGcrBarRetransmission(gcrGroupAddress.value(),
1719 recipientMld,
1720 tid)
1721 : GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld);
1722 if (retransmitBar)
1723 {
1724 NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
1725 /**
1726 * The BlockAckReq must be sent on the same link as the data frames to avoid issues.
1727 * As an example, assume that an A-MPDU is sent on link 0, the BlockAck timer
1728 * expires and the BlockAckReq is sent on another link (e.g., on link 1). When the
1729 * originator processes the BlockAck response, it will not interpret a '0' in the
1730 * bitmap corresponding to the transmitted MPDUs as a negative acknowledgment,
1731 * because the BlockAck is received on a different link than the one on which the
1732 * MPDUs are (still) inflight. Hence, such MPDUs stay inflight and are not
1733 * retransmitted.
1734 */
1735 if (isBar)
1736 {
1737 psdu->GetHeader(0).SetRetry();
1738 }
1739 else
1740 {
1741 // missed block ack after data frame with Implicit BAR Ack policy
1742 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid);
1743 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1744 }
1745 }
1746 else
1747 {
1748 NS_LOG_DEBUG("Missed Block Ack, do not transmit a BlockAckReq");
1749 // if a BA agreement exists, we can get here if there is no outstanding
1750 // MPDU whose lifetime has not expired yet.
1751 if (isBar)
1752 {
1753 DequeuePsdu(psdu);
1754 }
1755 if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
1756 {
1757 // schedule a BlockAckRequest to be sent only if there are data frames queued
1758 // for this recipient
1759 GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
1760 }
1761 }
1762 }
1763 else
1764 {
1765 // we have to retransmit the data frames, if needed
1766 GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
1767 }
1768}
1769
1770void
1772 Time durationId,
1773 WifiTxVector& blockAckTxVector,
1774 double rxSnr,
1775 std::optional<Mac48Address> gcrGroupAddr)
1776{
1777 NS_LOG_FUNCTION(this << durationId << blockAckTxVector << rxSnr << gcrGroupAddr.has_value());
1778
1779 WifiMacHeader hdr;
1781 auto addr1 = agreement.GetPeer();
1782 if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
1783 {
1784 addr1 = *originator;
1785 }
1786 hdr.SetAddr1(addr1);
1787 hdr.SetAddr2(m_self);
1788 hdr.SetDsNotFrom();
1789 hdr.SetDsNotTo();
1790
1791 CtrlBAckResponseHeader blockAck;
1792 blockAck.SetType(agreement.GetBlockAckType());
1793 if (gcrGroupAddr.has_value())
1794 {
1795 blockAck.SetGcrGroupAddress(gcrGroupAddr.value());
1796 }
1797 blockAck.SetTidInfo(agreement.GetTid());
1798 agreement.FillBlockAckBitmap(blockAck);
1799
1800 Ptr<Packet> packet = Create<Packet>();
1801 packet->AddHeader(blockAck);
1802 Ptr<WifiPsdu> psdu = GetWifiPsdu(Create<WifiMpdu>(packet, hdr), blockAckTxVector);
1803
1804 // 802.11-2016, Section 9.2.5.7: In a BlockAck frame transmitted in response
1805 // to a BlockAckReq frame or transmitted in response to a frame containing an
1806 // implicit block ack request, the Duration/ID field is set to the value obtained
1807 // from the Duration/ ID field of the frame that elicited the response minus the
1808 // time, in microseconds between the end of the PPDU carrying the frame that
1809 // elicited the response and the end of the PPDU carrying the BlockAck frame.
1810 Time baDurationId = durationId - m_phy->GetSifs() -
1811 WifiPhy::CalculateTxDuration(psdu, blockAckTxVector, m_phy->GetPhyBand());
1812 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016)
1813 if (baDurationId.IsStrictlyNegative())
1814 {
1815 baDurationId = Seconds(0);
1816 }
1817 psdu->GetHeader(0).SetDuration(baDurationId);
1818
1819 SnrTag tag;
1820 tag.Set(rxSnr);
1821 psdu->GetPayload(0)->AddPacketTag(tag);
1822
1823 ForwardPsduDown(psdu, blockAckTxVector);
1824}
1825
1826void
1828 RxSignalInfo rxSignalInfo,
1829 const WifiTxVector& txVector,
1830 bool inAmpdu)
1831{
1832 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
1833
1834 // The received MPDU is either broadcast or addressed to this station
1835 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1836
1837 double rxSnr = rxSignalInfo.snr;
1838 const WifiMacHeader& hdr = mpdu->GetHeader();
1839
1840 if (hdr.IsCtl())
1841 {
1842 if (hdr.IsCts() && m_txTimer.IsRunning() &&
1843 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psdu)
1844 {
1845 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1846 NS_ASSERT(hdr.GetAddr1() == m_self);
1847
1848 Mac48Address sender = m_psdu->GetAddr1();
1849 NS_LOG_DEBUG("Received CTS from=" << sender);
1850
1851 SnrTag tag;
1852 mpdu->GetPacket()->PeekPacketTag(tag);
1853 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1854 GetWifiRemoteStationManager()->ReportRtsOk(m_psdu->GetHeader(0),
1855 rxSnr,
1856 txVector.GetMode(),
1857 tag.Get());
1858
1859 m_txTimer.Cancel();
1860 m_channelAccessManager->NotifyCtsTimeoutResetNow();
1862 }
1863 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1864 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK && hdr.GetAddr1() == m_self)
1865 {
1866 Mac48Address sender = hdr.GetAddr2();
1867 NS_LOG_DEBUG("Received BlockAck from=" << sender);
1868 m_txTimer.GotResponseFrom(sender);
1869
1870 SnrTag tag;
1871 mpdu->GetPacket()->PeekPacketTag(tag);
1872
1873 // notify the Block Ack Manager
1874 CtrlBAckResponseHeader blockAck;
1875 mpdu->GetPacket()->PeekHeader(blockAck);
1876 uint8_t tid = blockAck.GetTidInfo();
1877 if (blockAck.IsGcr())
1878 {
1879 const auto& gcrMembers = m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(
1880 blockAck.GetGcrGroupAddress());
1881 const auto ret = GetBaManager(tid)->NotifyGotGcrBlockAck(
1882 m_linkId,
1883 blockAck,
1884 m_mac->GetMldAddress(sender).value_or(sender),
1885 gcrMembers);
1886
1887 if (ret.has_value())
1888 {
1889 for (const auto& sender : gcrMembers)
1890 {
1891 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1892 ret->first,
1893 ret->second,
1894 rxSnr,
1895 tag.Get(),
1896 m_txParams.m_txVector);
1897 }
1898 }
1899 }
1900 else
1901 {
1902 const auto [nSuccessful, nFailed] = GetBaManager(tid)->NotifyGotBlockAck(
1903 m_linkId,
1904 blockAck,
1905 m_mac->GetMldAddress(sender).value_or(sender),
1906 {tid});
1907
1908 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1909 nSuccessful,
1910 nFailed,
1911 rxSnr,
1912 tag.Get(),
1913 m_txParams.m_txVector);
1914 }
1915
1916 // cancel the timer
1917 m_txTimer.Cancel();
1918 m_channelAccessManager->NotifyAckTimeoutResetNow();
1919
1920 // Reset the CW, unless m_edca is null, which means we were given the right to transmit
1921 // a frame (e.g., we received a PS-Poll frame)
1922 if (m_edca)
1923 {
1924 m_edca->ResetCw(m_linkId);
1925 }
1926
1927 // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
1928 if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
1929 {
1931 }
1932 m_psdu = nullptr;
1933 if (m_edca)
1934 {
1936 }
1937 else
1938 {
1939 m_sentFrameTo.clear();
1940 }
1941 }
1942 else if (hdr.IsBlockAckReq())
1943 {
1944 NS_ASSERT(hdr.GetAddr1() == m_self);
1945 NS_ABORT_MSG_IF(inAmpdu, "BlockAckReq in A-MPDU is not supported");
1946
1947 auto sender = hdr.GetAddr2();
1948 NS_LOG_DEBUG("Received BlockAckReq from=" << sender);
1949
1950 CtrlBAckRequestHeader blockAckReq;
1951 mpdu->GetPacket()->PeekHeader(blockAckReq);
1952 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1953 const auto tid = blockAckReq.GetTidInfo();
1954
1955 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(
1956 sender,
1957 tid,
1958 blockAckReq.IsGcr() ? std::optional{blockAckReq.GetGcrGroupAddress()}
1959 : std::nullopt);
1960 if (!agreement)
1961 {
1962 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1963 return;
1964 }
1965
1966 GetBaManager(tid)->NotifyGotBlockAckRequest(
1967 m_mac->GetMldAddress(sender).value_or(sender),
1968 tid,
1969 blockAckReq.GetStartingSequence(),
1970 blockAckReq.IsGcr() ? std::optional{blockAckReq.GetGcrGroupAddress()}
1971 : std::nullopt);
1972
1973 NS_LOG_DEBUG("Schedule Block Ack");
1975 m_phy->GetSifs(),
1977 this,
1978 *agreement,
1979 hdr.IsPsPoll() ? Seconds(0) : hdr.GetDuration(),
1980 GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
1981 rxSnr,
1982 blockAckReq.IsGcr() ? std::optional{blockAckReq.GetGcrGroupAddress()}
1983 : std::nullopt);
1984 }
1985 else
1986 {
1987 // the received control frame cannot be handled here
1988 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1989 }
1990 return;
1991 }
1992
1993 if (const auto isGroup = IsGroupcast(hdr.GetAddr1());
1994 hdr.IsQosData() && hdr.HasData() &&
1995 ((hdr.GetAddr1() == m_self) || (isGroup && (inAmpdu || !mpdu->GetHeader().IsQosNoAck()))))
1996 {
1997 const auto tid = hdr.GetQosTid();
1998
1999 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(
2000 hdr.GetAddr2(),
2001 tid,
2002 isGroup ? std::optional{hdr.IsQosAmsdu() ? mpdu->begin()->second.GetDestinationAddr()
2003 : hdr.GetAddr1()}
2004 : std::nullopt);
2005 if (agreement)
2006 {
2007 // a Block Ack agreement has been established
2008 NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
2009
2010 GetBaManager(tid)->NotifyGotMpdu(mpdu);
2011
2012 if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
2013 {
2014 NS_LOG_DEBUG("Schedule Normal Ack");
2015 Simulator::Schedule(m_phy->GetSifs(),
2017 this,
2018 hdr,
2019 txVector,
2020 rxSnr);
2021 }
2022 return;
2023 }
2024 // We let the QosFrameExchangeManager handle QoS data frame not belonging
2025 // to a Block Ack agreement
2026 }
2027
2028 if (hdr.IsMgt() && hdr.IsAction())
2029 {
2030 ReceiveMgtAction(mpdu, txVector);
2031 }
2032
2033 if (IsGroupcast(hdr.GetAddr1()) && hdr.IsQosData() && hdr.IsQosAmsdu() &&
2034 !m_mac->GetRobustAVStreamingSupported())
2035 {
2036 return;
2037 }
2038
2039 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
2040}
2041
2042void
2044{
2045 NS_LOG_FUNCTION(this << *mpdu << txVector);
2046
2047 NS_ASSERT(mpdu->GetHeader().IsAction());
2048 const auto from = mpdu->GetOriginal()->GetHeader().GetAddr2();
2049
2050 WifiActionHeader actionHdr;
2051 auto packet = mpdu->GetPacket()->Copy();
2052 packet->RemoveHeader(actionHdr);
2053
2054 // compute the time to transmit the Ack
2055 const auto ackTxVector =
2056 GetWifiRemoteStationManager()->GetAckTxVector(mpdu->GetHeader().GetAddr2(), txVector);
2057 const auto ackTxTime =
2058 WifiPhy::CalculateTxDuration(GetAckSize(), ackTxVector, m_phy->GetPhyBand());
2059
2060 switch (actionHdr.GetCategory())
2061 {
2063
2064 switch (actionHdr.GetAction().blockAck)
2065 {
2067 MgtAddBaRequestHeader reqHdr;
2068 packet->RemoveHeader(reqHdr);
2069
2070 // We've received an ADDBA Request. Our policy here is to automatically accept it,
2071 // so we get the ADDBA Response on its way as soon as we finish transmitting the Ack,
2072 // to avoid to concurrently send Ack and ADDBA Response in case of multi-link devices
2073 Simulator::Schedule(m_phy->GetSifs() + ackTxTime,
2075 this,
2076 reqHdr,
2077 from);
2078 // This frame is now completely dealt with, so we're done.
2079 return;
2080 }
2082 MgtAddBaResponseHeader respHdr;
2083 packet->RemoveHeader(respHdr);
2084
2085 // We've received an ADDBA Response. Wait until we finish transmitting the Ack before
2086 // unblocking transmissions to the recipient, otherwise for multi-link devices the Ack
2087 // may be sent concurrently with a data frame containing an A-MPDU
2088 Simulator::Schedule(m_phy->GetSifs() + ackTxTime, [=, this]() {
2089 const auto recipient =
2090 GetWifiRemoteStationManager()->GetMldAddress(from).value_or(from);
2091 m_mac->GetQosTxop(respHdr.GetTid())->GotAddBaResponse(respHdr, recipient);
2092 GetBaManager(respHdr.GetTid())
2093 ->SetBlockAckInactivityCallback(
2094 MakeCallback(&HtFrameExchangeManager::SendDelbaFrame, this));
2095 });
2096 // This frame is now completely dealt with, so we're done.
2097 return;
2098 }
2100 MgtDelBaHeader delBaHdr;
2101 packet->RemoveHeader(delBaHdr);
2102 auto recipient = GetWifiRemoteStationManager()->GetMldAddress(from).value_or(from);
2103
2104 if (delBaHdr.IsByOriginator())
2105 {
2106 // This DELBA frame was sent by the originator, so
2107 // this means that an ingoing established
2108 // agreement exists in BlockAckManager and we need to
2109 // destroy it.
2110 GetBaManager(delBaHdr.GetTid())
2111 ->DestroyRecipientAgreement(recipient,
2112 delBaHdr.GetTid(),
2113 delBaHdr.GetGcrGroupAddress());
2114 }
2115 else
2116 {
2117 // We must have been the originator. We need to
2118 // tell the correct queue that the agreement has
2119 // been torn down
2120 m_mac->GetQosTxop(delBaHdr.GetTid())->GotDelBaFrame(&delBaHdr, recipient);
2121 }
2122 // This frame is now completely dealt with, so we're done.
2123 return;
2124 }
2125 default:
2126 NS_FATAL_ERROR("Unsupported Action field in Block Ack Action frame");
2127 }
2128 default:
2129 // Other action frames are not processed here
2130 ;
2131 }
2132}
2133
2134void
2136 const RxSignalInfo& rxSignalInfo,
2137 const WifiTxVector& txVector,
2138 const std::vector<bool>& perMpduStatus)
2139{
2141 this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
2142 << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](bool v) { return v; }));
2143
2144 std::set<uint8_t> tids = psdu->GetTids();
2145
2146 // Multi-TID A-MPDUs are not supported yet
2147 if (tids.size() == 1)
2148 {
2149 uint8_t tid = *tids.begin();
2150 WifiMacHeader::QosAckPolicy ackPolicy = psdu->GetAckPolicyForTid(tid);
2151 NS_ASSERT(psdu->GetNMpdus() > 1);
2152
2153 if (ackPolicy == WifiMacHeader::NORMAL_ACK)
2154 {
2155 // Normal Ack or Implicit Block Ack Request
2156 NS_LOG_DEBUG("Schedule Block Ack");
2157 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
2158 NS_ASSERT(agreement);
2159
2161 m_phy->GetSifs(),
2163 this,
2164 *agreement,
2165 psdu->GetDuration(),
2166 GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
2167 rxSignalInfo.snr,
2168 std::nullopt);
2169 }
2170 else if (psdu->GetAddr1().IsGroup() && (ackPolicy == WifiMacHeader::NO_ACK))
2171 {
2172 // groupcast A-MPDU received
2174
2175 /*
2176 * There might be pending MPDUs from a previous groupcast transmission
2177 * that have not been forwarded up yet (e.g. all transmission attempts
2178 * of a given MPDU have failed). For groupcast transmissions using GCR-UR service,
2179 * transmitter keeps advancing its window since there is no feedback from the
2180 * recipients. In order to forward up previously received groupcast MPDUs and avoid
2181 * following MPDUs not to be forwarded up, we flush the recipient window. The sequence
2182 * number to use can easily be deduced since sequence number of groupcast MPDUs are
2183 * consecutive.
2184 */
2185 const auto startSeq = psdu->GetHeader(0).GetSequenceNumber();
2186 const auto groupAddress = psdu->GetHeader(0).IsQosAmsdu()
2187 ? (*psdu->begin())->begin()->second.GetDestinationAddr()
2188 : psdu->GetAddr1();
2189 FlushGroupcastMpdus(groupAddress, psdu->GetAddr2(), tid, startSeq);
2190
2191 /*
2192 * In case all MPDUs of all following transmissions are corrupted or
2193 * if no following groupcast transmission happens, some groupcast MPDUs
2194 * of the currently received A-MPDU would never be forwarded up. To prevent this,
2195 * we schedule a flush of the recipient window once the MSDU lifetime limit elapsed.
2196 */
2197 const auto stopSeq = (startSeq + perMpduStatus.size()) % 4096;
2198 const auto maxDelay = m_mac->GetQosTxop(tid)->GetWifiMacQueue()->GetMaxDelay();
2200 Simulator::Schedule(maxDelay,
2202 this,
2203 groupAddress,
2204 psdu->GetAddr2(),
2205 tid,
2206 stopSeq);
2207 }
2208 }
2209}
2210
2211void
2213 const Mac48Address& originator,
2214 uint8_t tid,
2215 uint16_t seq)
2216{
2217 NS_LOG_FUNCTION(this << groupAddress << originator << tid << seq);
2218 // We can flush the recipient window by indicating the reception of an implicit GCR BAR
2219 GetBaManager(tid)->NotifyGotBlockAckRequest(originator, tid, seq, groupAddress);
2220}
2221
2222void
2224{
2225 NS_LOG_FUNCTION(this << mpdu);
2226 const auto tid = mpdu->GetHeader().GetQosTid();
2227 const auto groupAddress = mpdu->GetHeader().GetAddr1();
2228 if (!GetBaManager(tid)->IsGcrAgreementEstablished(
2229 groupAddress,
2230 tid,
2231 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(groupAddress)))
2232 {
2233 return;
2234 }
2235 GetBaManager(tid)->NotifyLastGcrUrTx(
2236 mpdu,
2237 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(groupAddress));
2238}
2239
2240} // namespace ns3
uint32_t v
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).
Mac48Address GetGcrGroupAddress() const
void SetStartingSequence(uint16_t seq)
Set the starting sequence number from the given raw sequence control field.
Headers for BlockAck response.
void SetGcrGroupAddress(const Mac48Address &address)
Set the GCR Group address (GCR variant only).
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...
Mac48Address GetGcrGroupAddress() const
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)
uint8_t m_linkId
the ID of the link this object is associated with
Ptr< WifiMac > m_mac
the MAC layer on this station
void SetTxNav(Ptr< const WifiMpdu > mpdu, const Time &txDuration)
Set the TXNAV upon sending an MPDU.
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.
Mac48Address GetAddress() const
Get the MAC address.
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 bool SendBufferedUnit(Mac48Address sender)
Send a buffered unit to the given sender, if any.
virtual void CtsTimeout(Ptr< WifiMpdu > rts, const WifiTxVector &txVector)
Called when the CTS timeout expires.
void DoCtsTimeout(const WifiPsduMap &psduMap)
Take required actions when the CTS timer fired after sending an (MU-)RTS to protect the given PSDU ma...
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.
void ReceiveFrameAfterPsPoll()
Take actions required when a frame is received from the associated AP after sending a PS-Poll frame.
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
Ptr< ApWifiMac > m_apMac
AP MAC layer pointer (null if not an AP).
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
std::unordered_set< Mac48Address, WifiAddressHash > GcrMembers
MAC addresses of member STAs of a GCR group.
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).
std::map< AgreementKey, Ptr< WifiMpdu > > m_pendingAddBaResp
pending ADDBA_RESPONSE frames indexed by agreement key
void FlushGroupcastMpdus(const Mac48Address &groupAddress, const Mac48Address &originator, uint8_t tid, uint16_t seq)
Perform required actions to ensure the receiver window is flushed when a groupcast A-MPDU is received...
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.
virtual std::optional< Mac48Address > NeedSetupGcrBlockAck(const WifiMacHeader &header)
A Block Ack agreement needs to be established prior to the transmission of a groupcast data packet us...
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...
void SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator, std::optional< Mac48Address > gcrGroupAddr)
Sends DELBA frame to cancel a block ack agreement with STA addressed by addr for TID tid.
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.
bool SendBufferedUnit(Mac48Address sender) override
Send a buffered unit to the given sender, if any.
void CalculateAcknowledgmentTime(WifiAcknowledgment *acknowledgment) const override
Calculate the time required to acknowledge a frame according to the given acknowledgment method.
EventId m_flushGroupcastMpdusEvent
the event to flush pending groupcast MPDUs from previously received A-MPDU
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...
void SendBlockAck(const RecipientBlockAckAgreement &agreement, Time durationId, WifiTxVector &blockAckTxVector, double rxSnr, std::optional< Mac48Address > gcrGroupAddr=std::nullopt)
Create a BlockAck frame with header equal to blockAck and start its transmission.
Ptr< MsduAggregator > m_msduAggregator
A-MSDU aggregator.
std::pair< Mac48Address, uint8_t > AgreementKey
agreement key typedef (MAC address and TID)
void NotifyLastGcrUrTx(Ptr< const WifiMpdu > mpdu) override
Notify the last (re)transmission of a groupcast MPDU using the GCR-UR service.
uint16_t GetBaAgreementStartingSequenceNumber(const WifiMacHeader &header)
Retrieve the starting sequence number for a BA agreement to be established.
an EUI-48 address
bool IsGroup() const
bool IsBroadcast() 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.
std::optional< Mac48Address > GetGcrGroupAddress() const
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.
std::optional< Mac48Address > GetGcrGroupAddress() const
uint8_t GetTid() const
Return the Traffic ID (TID).
bool IsByOriginator() const
Check if the initiator bit in the DELBA is set.
void SetGcrGroupAddress(const Mac48Address &address)
Set the GCR Group address.
void SetByOriginator()
Set the initiator bit in the DELBA.
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
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...
Time m_singleExchangeProtectionSurplus
additional time to protect beyond end of the immediate frame exchange in case of non-zero TXOP limit ...
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...
bool m_protectSingleExchange
true if the Duration/ID field in frames establishing protection only covers the immediate frame excha...
void DoDispose() override
Destructor implementation.
void AddBaResponseTimeout(Mac48Address recipient, uint8_t tid, std::optional< Mac48Address > gcrGroupAddr)
Callback when ADDBA response is not received after timeout.
Definition qos-txop.cc:806
void ResetBa(Mac48Address recipient, uint8_t tid, std::optional< Mac48Address > gcrGroupAddr)
Reset BA agreement after BA negotiation failed.
Definition qos-txop.cc:826
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:580
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:191
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:95
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition nstime.h:277
bool IsStrictlyNegative() const
Exactly equivalent to t < 0.
Definition nstime.h:332
a unique identifier for an interface.
Definition type-id.h:50
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition type-id.cc:999
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 IsQosAmsdu() const
Check if IsQosData() is true and the A-MSDU present bit is set in the QoS control field.
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 IsQosData() is true and 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 IsPsPoll() const
Return true if the header is a PS-POLL header.
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.
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1574
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1567
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...
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:194
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
#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:627
#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:454
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1307
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
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:64
@ STA
Definition wifi-mac.h:59
@ AP
Definition wifi-mac.h:60
@ 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:463
std::unordered_map< uint16_t, Ptr< WifiPsdu > > WifiPsduMap
Map of PSDUs indexed by STA-ID.
Definition wifi-mac.h:78
bool IsGroupcast(const Mac48Address &adr)
Check whether a MAC destination address corresponds to a groupcast transmission.
uint32_t GetBlockAckRequestSize(BlockAckReqType type)
Return the total BlockAckRequest size (including FCS trailer).
Definition wifi-utils.cc:69
@ WIFI_MAC_MGT_ACTION
@ WIFI_MAC_CTL_BACKRESP
static constexpr uint16_t SEQNO_SPACE_SIZE
Size of the space of sequence numbers.
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition wifi-utils.cc:59
std::tuple< WifiContainerQueueType, WifiRcvAddr, Mac48Address, std::optional< uint8_t > > WifiContainerQueueId
Tuple (queue type, receiver address type, Address, TID) identifying a container queue.
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition wifi-utils.cc:51
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU).
bool IsGcr(Ptr< WifiMac > mac, const WifiMacHeader &hdr)
Return whether a given packet is transmitted using the GCR service.
const std::map< AcIndex, WifiAc > wifiAcList
Map containing the four ACs in increasing order of priority (according to Table 10-1 "UP-to-AC Mappin...
Definition qos-utils.cc:115
ns3::Time timeout
RxSignalInfo structure containing info on the received signal.
Definition wifi-types.h:84
double snr
SNR in linear scale.
Definition wifi-types.h:85
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