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