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