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)
628 {
629 if (const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
630 currAddr1 == m_mac->GetMldAddress(currAddr1))
631 {
632 // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
633 // and move to the appropriate container queue
634 DequeueMpdu(selectedBar);
635 const auto addr1 =
636 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(
637 currAddr1);
638 selectedBar->GetHeader().SetAddr1(addr1);
639 selectedBar->GetHeader().SetAddr2(m_self);
640 queue->Enqueue(selectedBar);
641 }
642 }
643
644 return selectedBar;
645}
646
647bool
649 Time availableTime,
650 bool initialFrame)
651{
652 NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
653
654 // First, check if there is a BAR to be transmitted
655 if (!mpdu->GetHeader().IsBlockAckReq())
656 {
657 NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
658 return false;
659 }
660
661 // Prepare the TX parameters. Note that the default ack manager expects the
662 // data TxVector in the m_txVector field to compute the BlockAck TxVector.
663 // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
664 // a few lines below.
665 WifiTxParameters txParams;
666 txParams.m_txVector =
667 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
668
669 if (!TryAddMpdu(mpdu, txParams, availableTime))
670 {
671 NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
672 return false;
673 }
674
676
677 // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
678 auto blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
679 txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
680
681 // we can transmit the BlockAckReq frame
682 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
683 return true;
684}
685
686bool
688 Time availableTime,
689 bool initialFrame)
690{
691 NS_ASSERT(peekedItem && peekedItem->GetHeader().IsQosData() &&
692 !peekedItem->GetHeader().GetAddr1().IsBroadcast() && !peekedItem->IsFragment());
693 NS_LOG_FUNCTION(this << *peekedItem << availableTime << initialFrame);
694
695 Ptr<QosTxop> edca = m_mac->GetQosTxop(peekedItem->GetHeader().GetQosTid());
696 WifiTxParameters txParams;
697 txParams.m_txVector =
698 GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
699 Ptr<WifiMpdu> mpdu =
700 edca->GetNextMpdu(m_linkId, peekedItem, txParams, availableTime, initialFrame);
701
702 if (!mpdu)
703 {
704 NS_LOG_DEBUG("Not enough time to transmit a frame");
705 return false;
706 }
707
708 // try A-MPDU aggregation
709 std::vector<Ptr<WifiMpdu>> mpduList =
710 m_mpduAggregator->GetNextAmpdu(mpdu, txParams, availableTime);
711 NS_ASSERT(txParams.m_acknowledgment);
712
713 if (mpduList.size() > 1)
714 {
715 // A-MPDU aggregation succeeded
716 SendPsduWithProtection(Create<WifiPsdu>(std::move(mpduList)), txParams);
717 }
718 else if (txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
719 {
720 // a QoS data frame using the Block Ack policy can be followed by a BlockAckReq
721 // frame and a BlockAck frame. Such a sequence is handled by the HT FEM
722 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
723 }
724 else
725 {
726 // transmission can be handled by the base FEM
727 SendMpduWithProtection(mpdu, txParams);
728 }
729
730 return true;
731}
732
733void
735{
736 NS_LOG_FUNCTION(this << acknowledgment);
737 NS_ASSERT(acknowledgment);
738
739 if (acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
740 {
741 auto blockAcknowledgment = static_cast<WifiBlockAck*>(acknowledgment);
742 auto baTxDuration =
743 WifiPhy::CalculateTxDuration(GetBlockAckSize(blockAcknowledgment->baType),
744 blockAcknowledgment->blockAckTxVector,
745 m_phy->GetPhyBand());
746 blockAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + baTxDuration;
747 }
748 else if (acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
749 {
750 auto barBlockAcknowledgment = static_cast<WifiBarBlockAck*>(acknowledgment);
751 auto barTxDuration =
752 WifiPhy::CalculateTxDuration(GetBlockAckRequestSize(barBlockAcknowledgment->barType),
753 barBlockAcknowledgment->blockAckReqTxVector,
754 m_phy->GetPhyBand());
755 auto baTxDuration =
756 WifiPhy::CalculateTxDuration(GetBlockAckSize(barBlockAcknowledgment->baType),
757 barBlockAcknowledgment->blockAckTxVector,
758 m_phy->GetPhyBand());
759 barBlockAcknowledgment->acknowledgmentTime =
760 2 * m_phy->GetSifs() + barTxDuration + baTxDuration;
761 }
762 else
763 {
765 }
766}
767
768void
770{
771 ForwardPsduDown(GetWifiPsdu(mpdu, txVector), txVector);
772}
773
776{
777 return Create<WifiPsdu>(mpdu, false);
778}
779
780void
782{
783 NS_LOG_FUNCTION(this << *mpdu);
784
785 if (mpdu->GetHeader().IsQosData())
786 {
787 uint8_t tid = mpdu->GetHeader().GetQosTid();
788 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
789
790 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
791 {
792 // notify the BA manager that the MPDU was acknowledged
793 edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
794 // the BA manager fires the AckedMpdu trace source, so nothing else must be done
795 return;
796 }
797 }
798 else if (mpdu->GetHeader().IsAction())
799 {
800 auto addr1 = mpdu->GetHeader().GetAddr1();
801 auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
802 WifiActionHeader actionHdr;
803 Ptr<Packet> p = mpdu->GetPacket()->Copy();
804 p->RemoveHeader(actionHdr);
805 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
806 {
808 {
809 MgtDelBaHeader delBa;
810 p->PeekHeader(delBa);
811 auto tid = delBa.GetTid();
812 if (delBa.IsByOriginator())
813 {
814 GetBaManager(tid)->DestroyOriginatorAgreement(address,
815 tid,
816 delBa.GetGcrGroupAddress());
817 }
818 else
819 {
820 GetBaManager(tid)->DestroyRecipientAgreement(address,
821 tid,
822 delBa.GetGcrGroupAddress());
823 }
824 }
826 {
827 // Setup ADDBA response timeout
829 p->PeekHeader(addBa);
830 Ptr<QosTxop> edca = m_mac->GetQosTxop(addBa.GetTid());
831 Simulator::Schedule(edca->GetAddBaResponseTimeout(),
833 edca,
834 address,
835 addBa.GetTid(),
836 addBa.GetGcrGroupAddress());
837 }
839 {
840 // A recipient Block Ack agreement must exist
842 p->PeekHeader(addBa);
843 auto tid = addBa.GetTid();
845 GetBaManager(tid)->GetAgreementAsRecipient(address,
846 tid,
847 addBa.GetGcrGroupAddress()),
848 "Recipient BA agreement {" << address << ", " << +tid << "} not found");
849 m_pendingAddBaResp.erase({address, tid});
850 }
851 }
852 }
854}
855
856void
858{
859 NS_LOG_DEBUG(this);
860
863 {
864 // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
865 // be transmitted (as responses) the following within the current TXOP:
866 // f) Any number of BlockAckReq frames
867 // (Sec. 10.22.2.8 of 802.11-2016)
868 NS_LOG_DEBUG("Schedule a transmission from Block Ack Manager in a SIFS");
871
872 // TXOP limit is null, hence the txopDuration parameter is unused
874
876 {
878 }
879 m_sentFrameTo.clear();
880 }
881 else
882 {
884 }
885}
886
887void
889{
890 NS_LOG_FUNCTION(this << *mpdu);
891
892 if (mpdu->GetHeader().IsQosData())
893 {
894 GetBaManager(mpdu->GetHeader().GetQosTid())->NotifyDiscardedMpdu(mpdu);
895 }
896 else if (mpdu->GetHeader().IsAction())
897 {
898 WifiActionHeader actionHdr;
899 mpdu->GetPacket()->PeekHeader(actionHdr);
900 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
902 {
903 const auto tid = GetTid(mpdu->GetPacket(), mpdu->GetHeader());
904 auto recipient = mpdu->GetHeader().GetAddr1();
905 // if the recipient is an MLD, use its MLD address
906 if (auto mldAddr = GetWifiRemoteStationManager()->GetMldAddress(recipient))
907 {
908 recipient = *mldAddr;
909 }
910 auto p = mpdu->GetPacket()->Copy();
911 p->RemoveHeader(actionHdr);
913 p->PeekHeader(addBa);
914 if (auto agreement =
915 GetBaManager(tid)->GetAgreementAsOriginator(recipient,
916 tid,
917 addBa.GetGcrGroupAddress());
918 agreement && agreement->get().IsPending())
919 {
920 NS_LOG_DEBUG("No ACK after ADDBA request");
921 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
922 qosTxop->NotifyOriginatorAgreementNoReply(recipient,
923 tid,
924 addBa.GetGcrGroupAddress());
925 Simulator::Schedule(qosTxop->GetFailedAddBaTimeout(),
927 qosTxop,
928 recipient,
929 tid,
930 addBa.GetGcrGroupAddress());
931 }
932 }
933 }
934 // the MPDU may have been dropped (and dequeued) by the above call to the NotifyDiscardedMpdu
935 // method of the BlockAckManager with reason WIFI_MAC_DROP_QOS_OLD_PACKET; in such a case, we
936 // must not fire the dropped callback again (with reason WIFI_MAC_DROP_REACHED_RETRY_LIMIT)
937 if (mpdu->IsQueued())
938 {
940 }
941}
942
943void
945{
946 NS_LOG_FUNCTION(this << *mpdu);
947
948 if (mpdu->GetHeader().IsQosData())
949 {
950 uint8_t tid = mpdu->GetHeader().GetQosTid();
951 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
952
953 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
954 {
955 // notify the BA manager that the MPDU was not acknowledged
956 edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
957 return;
958 }
959 }
961}
962
963void
965{
966 NS_LOG_FUNCTION(this << *psdu);
967
968 const auto tids = psdu->GetTids();
969 const auto isGcr = IsGcr(m_mac, psdu->GetHeader(0));
970 auto agreementEstablished =
971 !tids.empty() /* no QoS data frame included */ &&
972 (isGcr ? GetBaManager(*tids.begin())
973 ->IsGcrAgreementEstablished(
974 psdu->GetHeader(0).GetAddr1(),
975 *tids.begin(),
976 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(
977 psdu->GetHeader(0).GetAddr1()))
978 : m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin())
979 .has_value());
980
981 if (!agreementEstablished)
982 {
984 return;
985 }
986
987 // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
988 auto mpduIt = psdu->end();
989
990 do
991 {
992 std::advance(mpduIt, -1);
993
994 const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
995 if (hdr.IsQosData())
996 {
997 uint8_t tid = hdr.GetQosTid();
998 agreementEstablished =
999 isGcr ? GetBaManager(tid)->IsGcrAgreementEstablished(
1000 psdu->GetHeader(0).GetAddr1(),
1001 tid,
1002 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(
1003 psdu->GetHeader(0).GetAddr1()))
1004 : m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), tid)
1005 .has_value();
1006 NS_ASSERT(agreementEstablished);
1007
1008 if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
1009 {
1010 // The MPDU has never been transmitted, so we can make its sequence
1011 // number available again if it is the highest sequence number
1012 // assigned by the MAC TX middle
1013 uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
1014
1015 if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
1016 {
1017 (*mpduIt)->UnassignSeqNo();
1018 m_txMiddle->SetSequenceNumberFor(&hdr);
1019
1020 NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
1021 << ", next sequence "
1022 "number for dest="
1023 << hdr.GetAddr1() << ",tid=" << +tid << " is "
1024 << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
1025 }
1026 }
1027 }
1028 } while (mpduIt != psdu->begin());
1029}
1030
1031Time
1033{
1034 NS_LOG_FUNCTION(this << txDuration << &txParams);
1035
1037 NS_ASSERT(txParams.m_acknowledgment &&
1038 txParams.m_acknowledgment->acknowledgmentTime.has_value());
1039
1040 const auto singleDurationId = *txParams.m_acknowledgment->acknowledgmentTime;
1041
1043 {
1044 return singleDurationId;
1045 }
1046
1047 // under multiple protection settings, if the TXOP limit is not null, Duration/ID
1048 // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
1049 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
1050 // of 802.11-2016)
1051 auto duration = std::max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
1052
1054 {
1055 duration = std::min(duration, singleDurationId + m_singleExchangeProtectionSurplus);
1056 }
1057
1058 return duration;
1059}
1060
1061void
1063{
1064 NS_LOG_FUNCTION(this << psdu << &txParams);
1065
1066 m_psdu = psdu;
1067 m_txParams = std::move(txParams);
1068
1069#ifdef NS3_BUILD_PROFILE_DEBUG
1070 // If protection is required, the MPDUs must be stored in some queue because
1071 // they are not put back in a queue if the RTS/CTS exchange fails
1073 {
1074 for (const auto& mpdu : *PeekPointer(m_psdu))
1075 {
1076 NS_ASSERT(mpdu->GetHeader().IsCtl() || mpdu->IsQueued());
1077 }
1078 }
1079#endif
1080
1081 // Make sure that the acknowledgment time has been computed, so that SendRts()
1082 // and SendCtsToSelf() can reuse this value.
1084
1085 if (!m_txParams.m_acknowledgment->acknowledgmentTime.has_value())
1086 {
1088 }
1089
1090 // Set QoS Ack policy
1092
1093 for (const auto& mpdu : *PeekPointer(m_psdu))
1094 {
1095 if (mpdu->IsQueued())
1096 {
1097 mpdu->SetInFlight(m_linkId);
1098 }
1099 }
1100
1102}
1103
1104void
1106{
1107 NS_LOG_FUNCTION(this);
1108 if (m_psdu)
1109 {
1111 m_sentRtsTo.clear();
1113 {
1114 SendPsdu();
1115 }
1116 else
1117 {
1119 }
1120 return;
1121 }
1123}
1124
1125void
1127{
1128 NS_LOG_FUNCTION(this << *rts << txVector);
1129
1130 if (!m_psdu)
1131 {
1132 // A CTS Timeout occurred when protecting a single MPDU is handled by the
1133 // parent classes
1135 return;
1136 }
1137
1139 m_psdu = nullptr;
1140}
1141
1142void
1144{
1145 NS_LOG_FUNCTION(this);
1146
1147 Time txDuration =
1149
1151
1153 {
1154 std::set<uint8_t> tids = m_psdu->GetTids();
1155 NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
1156
1157 if (m_mac->GetTypeOfStation() == AP && m_apMac->UseGcr(m_psdu->GetHeader(0)))
1158 {
1159 if (m_apMac->GetGcrManager()->KeepGroupcastQueued(*m_psdu->begin()))
1160 {
1161 // keep the groupcast frame in the queue for future retransmission
1162 Simulator::Schedule(txDuration + m_phy->GetSifs(), [=, this, psdu = m_psdu]() {
1163 NS_LOG_DEBUG("Prepare groupcast PSDU for retry");
1164 for (const auto& mpdu : *PeekPointer(psdu))
1165 {
1166 mpdu->ResetInFlight(m_linkId);
1167 // restore addr1 to the group address instead of the concealment address
1168 if (m_apMac->GetGcrManager()->UseConcealment(mpdu->GetHeader()))
1169 {
1170 mpdu->GetHeader().SetAddr1(mpdu->begin()->second.GetDestinationAddr());
1171 }
1172 mpdu->GetHeader().SetRetry();
1173 }
1174 });
1175 }
1176 else
1177 {
1178 if (m_apMac->GetGcrManager()->GetRetransmissionPolicy() ==
1180 {
1181 for (const auto& mpdu : *PeekPointer(m_psdu))
1182 {
1183 NotifyLastGcrUrTx(mpdu);
1184 }
1185 }
1187 }
1188 }
1189 else if (tids.empty() || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
1190 {
1191 // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
1193 }
1194
1195 Simulator::Schedule(txDuration, [=, this]() {
1197 m_psdu = nullptr;
1198 });
1199 }
1200 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
1201 {
1202 m_psdu->SetDuration(GetPsduDurationId(txDuration, m_txParams));
1203
1204 // the timeout duration is "aSIFSTime + aSlotTime + aRxPHYStartDelay, starting
1205 // at the PHY-TXEND.confirm primitive" (section 10.3.2.9 or 10.22.2.2 of 802.11-2016).
1206 // aRxPHYStartDelay equals the time to transmit the PHY header.
1207 auto blockAcknowledgment = static_cast<WifiBlockAck*>(m_txParams.m_acknowledgment.get());
1208
1209 Time timeout =
1210 txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
1211 WifiPhy::CalculatePhyPreambleAndHeaderDuration(blockAcknowledgment->blockAckTxVector);
1212 NS_ASSERT(!m_txTimer.IsRunning());
1213 m_txTimer.Set(WifiTxTimer::WAIT_BLOCK_ACK,
1214 timeout,
1215 {m_psdu->GetAddr1()},
1217 this,
1218 m_psdu,
1219 m_txParams.m_txVector);
1220 m_channelAccessManager->NotifyAckTimeoutStartNow(timeout);
1221 }
1222 else if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
1223 {
1224 m_psdu->SetDuration(GetPsduDurationId(txDuration, m_txParams));
1225
1226 // schedule the transmission of a BAR in a SIFS
1227 const auto tids = m_psdu->GetTids();
1228 NS_ABORT_MSG_IF(tids.size() > 1,
1229 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
1230 const auto tid = *tids.begin();
1231
1232 auto edca = m_mac->GetQosTxop(tid);
1233 const auto isGcr = IsGcr(m_mac, m_psdu->GetHeader(0));
1234 const auto& recipients =
1235 isGcr ? m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(m_psdu->GetAddr1())
1236 : GcrManager::GcrMembers{m_psdu->GetAddr1()};
1237 std::optional<Mac48Address> gcrGroupAddress{isGcr ? std::optional{m_psdu->GetAddr1()}
1238 : std::nullopt};
1239 for (const auto& recipient : recipients)
1240 {
1241 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid, gcrGroupAddress);
1242 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1243 }
1244
1245 if (isGcr)
1246 {
1247 Simulator::Schedule(txDuration + m_phy->GetSifs(), [=, this, psdu = m_psdu]() {
1248 NS_LOG_DEBUG("Restore group address of PSDU");
1249 for (const auto& mpdu : *PeekPointer(psdu))
1250 {
1251 // restore addr1 to the group address instead of the concealment address
1252 if (m_apMac->GetGcrManager()->UseConcealment(mpdu->GetHeader()))
1253 {
1254 mpdu->GetHeader().SetAddr1(mpdu->begin()->second.GetDestinationAddr());
1255 }
1256 }
1257 });
1258 }
1259
1260 Simulator::Schedule(txDuration, [=, this]() {
1261 TransmissionSucceeded();
1262 m_psdu = nullptr;
1263 });
1264 }
1265 else
1266 {
1267 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
1268 << m_txParams.m_acknowledgment.get() << ")");
1269 }
1270
1271 // transmit the PSDU
1272 if (m_psdu->GetNMpdus() > 1)
1273 {
1274 ForwardPsduDown(m_psdu, m_txParams.m_txVector);
1275 }
1276 else
1277 {
1278 ForwardMpduDown(*m_psdu->begin(), m_txParams.m_txVector);
1279 }
1280
1281 if (m_txTimer.IsRunning())
1282 {
1283 NS_ASSERT(m_sentFrameTo.empty());
1284 m_sentFrameTo = {m_psdu->GetAddr1()};
1285 }
1286
1287 if (m_txParams.m_acknowledgment->method == WifiAcknowledgment::NONE)
1288 {
1289 // we are done in case the A-MPDU does not require acknowledgment
1290 m_psdu = nullptr;
1291 }
1292}
1293
1294void
1295HtFrameExchangeManager::NotifyTxToEdca(Ptr<const WifiPsdu> psdu) const
1296{
1297 NS_LOG_FUNCTION(this << psdu);
1298
1299 for (const auto& mpdu : *PeekPointer(psdu))
1300 {
1301 auto& hdr = mpdu->GetHeader();
1302
1303 if (hdr.IsQosData() && hdr.HasData())
1304 {
1305 auto tid = hdr.GetQosTid();
1306 m_mac->GetQosTxop(tid)->CompleteMpduTx(mpdu);
1307 }
1308 }
1309}
1310
1311void
1312HtFrameExchangeManager::FinalizeMacHeader(Ptr<const WifiPsdu> psdu)
1313{
1314 NS_LOG_FUNCTION(this << psdu);
1315
1316 // use an array to avoid computing the queue size for every MPDU in the PSDU
1317 std::array<std::optional<uint8_t>, 8> queueSizeForTid;
1318
1319 for (const auto& mpdu : *PeekPointer(psdu))
1320 {
1321 WifiMacHeader& hdr = mpdu->GetHeader();
1322
1323 if (hdr.IsQosData())
1324 {
1325 uint8_t tid = hdr.GetQosTid();
1326 auto edca = m_mac->GetQosTxop(tid);
1327
1328 if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
1329 {
1330 // set the Queue Size subfield of the QoS Control field
1331 if (!queueSizeForTid[tid].has_value())
1332 {
1333 queueSizeForTid[tid] =
1334 edca->GetQosQueueSize(tid, mpdu->GetOriginal()->GetHeader().GetAddr1());
1335 }
1336
1337 hdr.SetQosEosp();
1338 hdr.SetQosQueueSize(queueSizeForTid[tid].value());
1339 }
1340
1341 if (m_mac->GetTypeOfStation() == AP && m_apMac->UseGcr(hdr) &&
1342 m_apMac->GetGcrManager()->UseConcealment(mpdu->GetHeader()))
1343 {
1344 const auto& gcrConcealmentAddress =
1345 m_apMac->GetGcrManager()->GetGcrConcealmentAddress();
1346 hdr.SetAddr1(gcrConcealmentAddress);
1347 }
1348 }
1349 }
1350
1351 QosFrameExchangeManager::FinalizeMacHeader(psdu);
1352}
1353
1354void
1355HtFrameExchangeManager::DequeuePsdu(Ptr<const WifiPsdu> psdu)
1356{
1357 NS_LOG_FUNCTION(this << *psdu);
1358 for (const auto& mpdu : *PeekPointer(psdu))
1359 {
1360 DequeueMpdu(mpdu);
1361 }
1362}
1363
1364void
1365HtFrameExchangeManager::ForwardPsduDown(Ptr<const WifiPsdu> psdu, WifiTxVector& txVector)
1366{
1367 NS_LOG_FUNCTION(this << psdu << txVector);
1368
1369 NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
1370 FinalizeMacHeader(psdu);
1371 NotifyTxToEdca(psdu);
1372 m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
1373
1374 if (psdu->IsAggregate())
1375 {
1376 txVector.SetAggregation(true);
1377 }
1378
1379 const auto txDuration = WifiPhy::CalculateTxDuration(psdu, txVector, m_phy->GetPhyBand());
1380 SetTxNav(*psdu->begin(), txDuration);
1381
1382 m_phy->Send(psdu, txVector);
1383}
1384
1385bool
1386HtFrameExchangeManager::IsWithinLimitsIfAddMpdu(Ptr<const WifiMpdu> mpdu,
1387 const WifiTxParameters& txParams,
1388 Time ppduDurationLimit) const
1389{
1390 NS_ASSERT(mpdu);
1391 NS_LOG_FUNCTION(this << *mpdu << &txParams << ppduDurationLimit);
1392
1393 Mac48Address receiver = mpdu->GetHeader().GetAddr1();
1394 uint32_t ampduSize = txParams.GetSize(receiver);
1395
1396 if (!txParams.LastAddedIsFirstMpdu(receiver))
1397 {
1398 // we are attempting to perform A-MPDU aggregation, hence we have to check
1399 // that we meet the limit on the max A-MPDU size
1400 uint8_t tid;
1401 const WifiTxParameters::PsduInfo* info;
1402
1403 if (mpdu->GetHeader().IsQosData())
1404 {
1405 tid = mpdu->GetHeader().GetQosTid();
1406 }
1407 else if ((info = txParams.GetPsduInfo(receiver)) && !info->seqNumbers.empty())
1408 {
1409 tid = info->seqNumbers.begin()->first;
1410 }
1411 else
1412 {
1413 NS_ABORT_MSG("Cannot aggregate a non-QoS data frame to an A-MPDU that does"
1414 " not contain any QoS data frame");
1415 }
1416
1417 WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1418
1419 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1420 {
1421 return false;
1422 }
1423 }
1424
1425 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1426}
1427
1428bool
1429HtFrameExchangeManager::IsWithinAmpduSizeLimit(uint32_t ampduSize,
1430 Mac48Address receiver,
1431 uint8_t tid,
1432 WifiModulationClass modulation) const
1433{
1434 NS_LOG_FUNCTION(this << ampduSize << receiver << +tid << modulation);
1435
1436 uint32_t maxAmpduSize = m_mpduAggregator->GetMaxAmpduSize(receiver, tid, modulation);
1437
1438 if (maxAmpduSize == 0)
1439 {
1440 NS_LOG_DEBUG("A-MPDU aggregation disabled");
1441 return false;
1442 }
1443
1444 if (ampduSize > maxAmpduSize)
1445 {
1446 NS_LOG_DEBUG("the frame does not meet the constraint on max A-MPDU size (" << maxAmpduSize
1447 << ")");
1448 return false;
1449 }
1450 return true;
1451}
1452
1453bool
1454HtFrameExchangeManager::TryAggregateMsdu(Ptr<const WifiMpdu> msdu,
1455 WifiTxParameters& txParams,
1456 Time availableTime) const
1457{
1458 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1459 NS_LOG_FUNCTION(this << *msdu << &txParams << availableTime);
1460
1461 // tentatively aggregate the given MPDU
1462 auto prevTxDuration = txParams.m_txDuration;
1463 txParams.AggregateMsdu(msdu);
1464 UpdateTxDuration(msdu->GetHeader().GetAddr1(), txParams);
1465
1466 // check if aggregating the given MSDU requires a different protection method
1467 NS_ASSERT(txParams.m_protection);
1468 auto protectionTime = txParams.m_protection->protectionTime;
1469
1470 std::unique_ptr<WifiProtection> protection;
1471 protection = GetProtectionManager()->TryAggregateMsdu(msdu, txParams);
1472 bool protectionSwapped = false;
1473
1474 if (protection)
1475 {
1476 // the protection method has changed, calculate the new protection time
1477 CalculateProtectionTime(protection.get());
1478 protectionTime = protection->protectionTime;
1479 // swap unique pointers, so that the txParams that is passed to the next
1480 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1481 txParams.m_protection.swap(protection);
1482 protectionSwapped = true;
1483 }
1484 NS_ASSERT(protectionTime.has_value());
1485
1486 // check if aggregating the given MSDU requires a different acknowledgment method
1487 NS_ASSERT(txParams.m_acknowledgment);
1488 auto acknowledgmentTime = txParams.m_acknowledgment->acknowledgmentTime;
1489
1490 std::unique_ptr<WifiAcknowledgment> acknowledgment;
1491 acknowledgment = GetAckManager()->TryAggregateMsdu(msdu, txParams);
1492 bool acknowledgmentSwapped = false;
1493
1494 if (acknowledgment)
1495 {
1496 // the acknowledgment method has changed, calculate the new acknowledgment time
1497 CalculateAcknowledgmentTime(acknowledgment.get());
1498 acknowledgmentTime = acknowledgment->acknowledgmentTime;
1499 // swap unique pointers, so that the txParams that is passed to the next
1500 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1501 txParams.m_acknowledgment.swap(acknowledgment);
1502 acknowledgmentSwapped = true;
1503 }
1504 NS_ASSERT(acknowledgmentTime.has_value());
1505
1506 Time ppduDurationLimit = Time::Min();
1507 if (availableTime != Time::Min())
1508 {
1509 ppduDurationLimit = availableTime - *protectionTime - *acknowledgmentTime;
1510 }
1511
1512 if (!IsWithinLimitsIfAggregateMsdu(msdu, txParams, ppduDurationLimit))
1513 {
1514 // adding MPDU failed, undo the addition of the MPDU and restore protection and
1515 // acknowledgment methods if they were swapped
1516 txParams.UndoAddMpdu();
1517 txParams.m_txDuration = prevTxDuration;
1518 if (protectionSwapped)
1519 {
1520 txParams.m_protection.swap(protection);
1521 }
1522 if (acknowledgmentSwapped)
1523 {
1524 txParams.m_acknowledgment.swap(acknowledgment);
1525 }
1526 return false;
1527 }
1528
1529 return true;
1530}
1531
1532bool
1533HtFrameExchangeManager::IsWithinLimitsIfAggregateMsdu(Ptr<const WifiMpdu> msdu,
1534 const WifiTxParameters& txParams,
1535 Time ppduDurationLimit) const
1536{
1537 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1538 NS_LOG_FUNCTION(this << *msdu << &txParams << ppduDurationLimit);
1539
1540 auto receiver = msdu->GetHeader().GetAddr1();
1541 auto tid = msdu->GetHeader().GetQosTid();
1542 auto modulation = txParams.m_txVector.GetModulationClass();
1543 auto psduInfo = txParams.GetPsduInfo(receiver);
1544 NS_ASSERT_MSG(psduInfo, "No PSDU info for receiver " << receiver);
1545
1546 // Check that the limit on A-MSDU size is met
1547 uint16_t maxAmsduSize = m_msduAggregator->GetMaxAmsduSize(receiver, tid, modulation);
1548
1549 if (maxAmsduSize == 0)
1550 {
1551 NS_LOG_DEBUG("A-MSDU aggregation disabled");
1552 return false;
1553 }
1554
1555 if (psduInfo->amsduSize > maxAmsduSize)
1556 {
1557 NS_LOG_DEBUG("No other MSDU can be aggregated: maximum A-MSDU size (" << maxAmsduSize
1558 << ") reached ");
1559 return false;
1560 }
1561
1562 const WifiTxParameters::PsduInfo* info = txParams.GetPsduInfo(msdu->GetHeader().GetAddr1());
1563 NS_ASSERT(info);
1564 auto ampduSize = txParams.GetSize(receiver);
1565
1566 if (info->ampduSize > 0)
1567 {
1568 // the A-MSDU being built is aggregated to other MPDUs in an A-MPDU.
1569 // Check that the limit on A-MPDU size is met.
1570 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1571 {
1572 return false;
1573 }
1574 }
1575
1576 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1577}
1578
1579void
1580HtFrameExchangeManager::BlockAckTimeout(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1581{
1582 NS_LOG_FUNCTION(this << *psdu << txVector);
1583
1584 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1585
1586 MissedBlockAck(psdu, txVector);
1587
1588 m_psdu = nullptr;
1589 TransmissionFailed();
1590}
1591
1592void
1593HtFrameExchangeManager::MissedBlockAck(Ptr<WifiPsdu> psdu, const WifiTxVector& txVector)
1594{
1595 NS_LOG_FUNCTION(this << psdu << txVector);
1596
1597 auto recipient = psdu->GetAddr1();
1598 auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
1599 bool isBar;
1600 uint8_t tid;
1601 std::optional<Mac48Address> gcrGroupAddress;
1602
1603 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
1604 {
1605 isBar = true;
1606 CtrlBAckRequestHeader baReqHdr;
1607 psdu->GetPayload(0)->PeekHeader(baReqHdr);
1608 tid = baReqHdr.GetTidInfo();
1609 if (baReqHdr.IsGcr())
1610 {
1611 gcrGroupAddress = baReqHdr.GetGcrGroupAddress();
1612 }
1613 }
1614 else
1615 {
1616 isBar = false;
1617 std::set<uint8_t> tids = psdu->GetTids();
1618 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
1619 NS_ASSERT(!tids.empty());
1620 tid = *tids.begin();
1621
1622 GetWifiRemoteStationManager()
1623 ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
1624
1625 if (auto droppedMpdu = DropMpduIfRetryLimitReached(psdu))
1626 {
1627 // notify remote station manager if at least an MPDU was dropped
1628 GetWifiRemoteStationManager()->ReportFinalDataFailed(droppedMpdu);
1629 }
1630 }
1631
1632 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1633
1634 if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
1635 {
1636 // we have to send a BlockAckReq, if needed
1637 const auto retransmitBar =
1638 gcrGroupAddress.has_value()
1639 ? GetBaManager(tid)->NeedGcrBarRetransmission(gcrGroupAddress.value(),
1640 recipientMld,
1641 tid)
1642 : GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld);
1643 if (retransmitBar)
1644 {
1645 NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
1646 /**
1647 * The BlockAckReq must be sent on the same link as the data frames to avoid issues.
1648 * As an example, assume that an A-MPDU is sent on link 0, the BlockAck timer
1649 * expires and the BlockAckReq is sent on another link (e.g., on link 1). When the
1650 * originator processes the BlockAck response, it will not interpret a '0' in the
1651 * bitmap corresponding to the transmitted MPDUs as a negative acknowledgment,
1652 * because the BlockAck is received on a different link than the one on which the
1653 * MPDUs are (still) inflight. Hence, such MPDUs stay inflight and are not
1654 * retransmitted.
1655 */
1656 if (isBar)
1657 {
1658 psdu->GetHeader(0).SetRetry();
1659 }
1660 else
1661 {
1662 // missed block ack after data frame with Implicit BAR Ack policy
1663 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid);
1664 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1665 }
1666 }
1667 else
1668 {
1669 NS_LOG_DEBUG("Missed Block Ack, do not transmit a BlockAckReq");
1670 // if a BA agreement exists, we can get here if there is no outstanding
1671 // MPDU whose lifetime has not expired yet.
1672 if (isBar)
1673 {
1674 DequeuePsdu(psdu);
1675 }
1676 if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
1677 {
1678 // schedule a BlockAckRequest to be sent only if there are data frames queued
1679 // for this recipient
1680 GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
1681 }
1682 }
1683 }
1684 else
1685 {
1686 // we have to retransmit the data frames, if needed
1687 GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
1688 }
1689}
1690
1691void
1692HtFrameExchangeManager::SendBlockAck(const RecipientBlockAckAgreement& agreement,
1693 Time durationId,
1694 WifiTxVector& blockAckTxVector,
1695 double rxSnr,
1696 std::optional<Mac48Address> gcrGroupAddr)
1697{
1698 NS_LOG_FUNCTION(this << durationId << blockAckTxVector << rxSnr << gcrGroupAddr.has_value());
1699
1700 WifiMacHeader hdr;
1702 auto addr1 = agreement.GetPeer();
1703 if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
1704 {
1705 addr1 = *originator;
1706 }
1707 hdr.SetAddr1(addr1);
1708 hdr.SetAddr2(m_self);
1709 hdr.SetDsNotFrom();
1710 hdr.SetDsNotTo();
1711
1712 CtrlBAckResponseHeader blockAck;
1713 blockAck.SetType(agreement.GetBlockAckType());
1714 if (gcrGroupAddr.has_value())
1715 {
1716 blockAck.SetGcrGroupAddress(gcrGroupAddr.value());
1717 }
1718 blockAck.SetTidInfo(agreement.GetTid());
1719 agreement.FillBlockAckBitmap(blockAck);
1720
1721 Ptr<Packet> packet = Create<Packet>();
1722 packet->AddHeader(blockAck);
1723 Ptr<WifiPsdu> psdu = GetWifiPsdu(Create<WifiMpdu>(packet, hdr), blockAckTxVector);
1724
1725 // 802.11-2016, Section 9.2.5.7: In a BlockAck frame transmitted in response
1726 // to a BlockAckReq frame or transmitted in response to a frame containing an
1727 // implicit block ack request, the Duration/ID field is set to the value obtained
1728 // from the Duration/ ID field of the frame that elicited the response minus the
1729 // time, in microseconds between the end of the PPDU carrying the frame that
1730 // elicited the response and the end of the PPDU carrying the BlockAck frame.
1731 Time baDurationId = durationId - m_phy->GetSifs() -
1732 WifiPhy::CalculateTxDuration(psdu, blockAckTxVector, m_phy->GetPhyBand());
1733 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016)
1734 if (baDurationId.IsStrictlyNegative())
1735 {
1736 baDurationId = Seconds(0);
1737 }
1738 psdu->GetHeader(0).SetDuration(baDurationId);
1739
1740 SnrTag tag;
1741 tag.Set(rxSnr);
1742 psdu->GetPayload(0)->AddPacketTag(tag);
1743
1744 ForwardPsduDown(psdu, blockAckTxVector);
1745}
1746
1747void
1748HtFrameExchangeManager::ReceiveMpdu(Ptr<const WifiMpdu> mpdu,
1749 RxSignalInfo rxSignalInfo,
1750 const WifiTxVector& txVector,
1751 bool inAmpdu)
1752{
1753 NS_LOG_FUNCTION(this << *mpdu << rxSignalInfo << txVector << inAmpdu);
1754
1755 // The received MPDU is either broadcast or addressed to this station
1756 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1757
1758 double rxSnr = rxSignalInfo.snr;
1759 const WifiMacHeader& hdr = mpdu->GetHeader();
1760
1761 if (hdr.IsCtl())
1762 {
1763 if (hdr.IsCts() && m_txTimer.IsRunning() &&
1764 m_txTimer.GetReason() == WifiTxTimer::WAIT_CTS && m_psdu)
1765 {
1766 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1767 NS_ASSERT(hdr.GetAddr1() == m_self);
1768
1769 Mac48Address sender = m_psdu->GetAddr1();
1770 NS_LOG_DEBUG("Received CTS from=" << sender);
1771
1772 SnrTag tag;
1773 mpdu->GetPacket()->PeekPacketTag(tag);
1774 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1775 GetWifiRemoteStationManager()->ReportRtsOk(m_psdu->GetHeader(0),
1776 rxSnr,
1777 txVector.GetMode(),
1778 tag.Get());
1779
1780 m_txTimer.Cancel();
1781 m_channelAccessManager->NotifyCtsTimeoutResetNow();
1782 ProtectionCompleted();
1783 }
1784 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1785 m_txTimer.GetReason() == WifiTxTimer::WAIT_BLOCK_ACK && hdr.GetAddr1() == m_self)
1786 {
1787 Mac48Address sender = hdr.GetAddr2();
1788 NS_LOG_DEBUG("Received BlockAck from=" << sender);
1789 m_txTimer.GotResponseFrom(sender);
1790
1791 SnrTag tag;
1792 mpdu->GetPacket()->PeekPacketTag(tag);
1793
1794 // notify the Block Ack Manager
1795 CtrlBAckResponseHeader blockAck;
1796 mpdu->GetPacket()->PeekHeader(blockAck);
1797 uint8_t tid = blockAck.GetTidInfo();
1798 if (blockAck.IsGcr())
1799 {
1800 const auto& gcrMembers = m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(
1801 blockAck.GetGcrGroupAddress());
1802 const auto ret = GetBaManager(tid)->NotifyGotGcrBlockAck(
1803 m_linkId,
1804 blockAck,
1805 m_mac->GetMldAddress(sender).value_or(sender),
1806 gcrMembers);
1807
1808 if (ret.has_value())
1809 {
1810 for (const auto& sender : gcrMembers)
1811 {
1812 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1813 ret->first,
1814 ret->second,
1815 rxSnr,
1816 tag.Get(),
1817 m_txParams.m_txVector);
1818 }
1819 }
1820 }
1821 else
1822 {
1823 const auto [nSuccessful, nFailed] = GetBaManager(tid)->NotifyGotBlockAck(
1824 m_linkId,
1825 blockAck,
1826 m_mac->GetMldAddress(sender).value_or(sender),
1827 {tid});
1828
1829 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1830 nSuccessful,
1831 nFailed,
1832 rxSnr,
1833 tag.Get(),
1834 m_txParams.m_txVector);
1835 }
1836
1837 // cancel the timer
1838 m_txTimer.Cancel();
1839 m_channelAccessManager->NotifyAckTimeoutResetNow();
1840
1841 // Reset the CW
1842 m_edca->ResetCw(m_linkId);
1843
1844 // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
1845 if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
1846 {
1847 DequeuePsdu(m_psdu);
1848 }
1849 m_psdu = nullptr;
1850 TransmissionSucceeded();
1851 }
1852 else if (hdr.IsBlockAckReq())
1853 {
1854 NS_ASSERT(hdr.GetAddr1() == m_self);
1855 NS_ABORT_MSG_IF(inAmpdu, "BlockAckReq in A-MPDU is not supported");
1856
1857 auto sender = hdr.GetAddr2();
1858 NS_LOG_DEBUG("Received BlockAckReq from=" << sender);
1859
1860 CtrlBAckRequestHeader blockAckReq;
1861 mpdu->GetPacket()->PeekHeader(blockAckReq);
1862 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1863 const auto tid = blockAckReq.GetTidInfo();
1864
1865 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(
1866 sender,
1867 tid,
1868 blockAckReq.IsGcr() ? std::optional{blockAckReq.GetGcrGroupAddress()}
1869 : std::nullopt);
1870 if (!agreement)
1871 {
1872 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1873 return;
1874 }
1875
1876 GetBaManager(tid)->NotifyGotBlockAckRequest(
1877 m_mac->GetMldAddress(sender).value_or(sender),
1878 tid,
1879 blockAckReq.GetStartingSequence(),
1880 blockAckReq.IsGcr() ? std::optional{blockAckReq.GetGcrGroupAddress()}
1881 : std::nullopt);
1882
1883 NS_LOG_DEBUG("Schedule Block Ack");
1884 Simulator::Schedule(
1885 m_phy->GetSifs(),
1886 &HtFrameExchangeManager::SendBlockAck,
1887 this,
1888 *agreement,
1889 hdr.GetDuration(),
1890 GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
1891 rxSnr,
1892 blockAckReq.IsGcr() ? std::optional{blockAckReq.GetGcrGroupAddress()}
1893 : std::nullopt);
1894 }
1895 else
1896 {
1897 // the received control frame cannot be handled here
1898 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1899 }
1900 return;
1901 }
1902
1903 if (const auto isGroup = IsGroupcast(hdr.GetAddr1());
1904 hdr.IsQosData() && hdr.HasData() &&
1905 ((hdr.GetAddr1() == m_self) || (isGroup && (inAmpdu || !mpdu->GetHeader().IsQosNoAck()))))
1906 {
1907 const auto tid = hdr.GetQosTid();
1908
1909 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(
1910 hdr.GetAddr2(),
1911 tid,
1912 isGroup ? std::optional{hdr.IsQosAmsdu() ? mpdu->begin()->second.GetDestinationAddr()
1913 : hdr.GetAddr1()}
1914 : std::nullopt);
1915 if (agreement)
1916 {
1917 // a Block Ack agreement has been established
1918 NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
1919
1920 GetBaManager(tid)->NotifyGotMpdu(mpdu);
1921
1922 if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
1923 {
1924 NS_LOG_DEBUG("Schedule Normal Ack");
1925 Simulator::Schedule(m_phy->GetSifs(),
1926 &HtFrameExchangeManager::SendNormalAck,
1927 this,
1928 hdr,
1929 txVector,
1930 rxSnr);
1931 }
1932 return;
1933 }
1934 // We let the QosFrameExchangeManager handle QoS data frame not belonging
1935 // to a Block Ack agreement
1936 }
1937
1938 if (hdr.IsMgt() && hdr.IsAction())
1939 {
1940 ReceiveMgtAction(mpdu, txVector);
1941 }
1942
1943 if (IsGroupcast(hdr.GetAddr1()) && hdr.IsQosData() && hdr.IsQosAmsdu() &&
1944 !m_mac->GetRobustAVStreamingSupported())
1945 {
1946 return;
1947 }
1948
1949 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1950}
1951
1952void
1953HtFrameExchangeManager::ReceiveMgtAction(Ptr<const WifiMpdu> mpdu, const WifiTxVector& txVector)
1954{
1955 NS_LOG_FUNCTION(this << *mpdu << txVector);
1956
1957 NS_ASSERT(mpdu->GetHeader().IsAction());
1958 const auto from = mpdu->GetOriginal()->GetHeader().GetAddr2();
1959
1960 WifiActionHeader actionHdr;
1961 auto packet = mpdu->GetPacket()->Copy();
1962 packet->RemoveHeader(actionHdr);
1963
1964 // compute the time to transmit the Ack
1965 const auto ackTxVector =
1966 GetWifiRemoteStationManager()->GetAckTxVector(mpdu->GetHeader().GetAddr2(), txVector);
1967 const auto ackTxTime =
1968 WifiPhy::CalculateTxDuration(GetAckSize(), ackTxVector, m_phy->GetPhyBand());
1969
1970 switch (actionHdr.GetCategory())
1971 {
1972 case WifiActionHeader::BLOCK_ACK:
1973
1974 switch (actionHdr.GetAction().blockAck)
1975 {
1976 case WifiActionHeader::BLOCK_ACK_ADDBA_REQUEST: {
1977 MgtAddBaRequestHeader reqHdr;
1978 packet->RemoveHeader(reqHdr);
1979
1980 // We've received an ADDBA Request. Our policy here is to automatically accept it,
1981 // so we get the ADDBA Response on its way as soon as we finish transmitting the Ack,
1982 // to avoid to concurrently send Ack and ADDBA Response in case of multi-link devices
1983 Simulator::Schedule(m_phy->GetSifs() + ackTxTime,
1984 &HtFrameExchangeManager::SendAddBaResponse,
1985 this,
1986 reqHdr,
1987 from);
1988 // This frame is now completely dealt with, so we're done.
1989 return;
1990 }
1991 case WifiActionHeader::BLOCK_ACK_ADDBA_RESPONSE: {
1992 MgtAddBaResponseHeader respHdr;
1993 packet->RemoveHeader(respHdr);
1994
1995 // We've received an ADDBA Response. Wait until we finish transmitting the Ack before
1996 // unblocking transmissions to the recipient, otherwise for multi-link devices the Ack
1997 // may be sent concurrently with a data frame containing an A-MPDU
1998 Simulator::Schedule(m_phy->GetSifs() + ackTxTime, [=, this]() {
1999 const auto recipient =
2000 GetWifiRemoteStationManager()->GetMldAddress(from).value_or(from);
2001 m_mac->GetQosTxop(respHdr.GetTid())->GotAddBaResponse(respHdr, recipient);
2002 GetBaManager(respHdr.GetTid())
2003 ->SetBlockAckInactivityCallback(
2004 MakeCallback(&HtFrameExchangeManager::SendDelbaFrame, this));
2005 });
2006 // This frame is now completely dealt with, so we're done.
2007 return;
2008 }
2009 case WifiActionHeader::BLOCK_ACK_DELBA: {
2010 MgtDelBaHeader delBaHdr;
2011 packet->RemoveHeader(delBaHdr);
2012 auto recipient = GetWifiRemoteStationManager()->GetMldAddress(from).value_or(from);
2013
2014 if (delBaHdr.IsByOriginator())
2015 {
2016 // This DELBA frame was sent by the originator, so
2017 // this means that an ingoing established
2018 // agreement exists in BlockAckManager and we need to
2019 // destroy it.
2020 GetBaManager(delBaHdr.GetTid())
2021 ->DestroyRecipientAgreement(recipient,
2022 delBaHdr.GetTid(),
2023 delBaHdr.GetGcrGroupAddress());
2024 }
2025 else
2026 {
2027 // We must have been the originator. We need to
2028 // tell the correct queue that the agreement has
2029 // been torn down
2030 m_mac->GetQosTxop(delBaHdr.GetTid())->GotDelBaFrame(&delBaHdr, recipient);
2031 }
2032 // This frame is now completely dealt with, so we're done.
2033 return;
2034 }
2035 default:
2036 NS_FATAL_ERROR("Unsupported Action field in Block Ack Action frame");
2037 }
2038 default:
2039 // Other action frames are not processed here
2040 ;
2041 }
2042}
2043
2044void
2045HtFrameExchangeManager::EndReceiveAmpdu(Ptr<const WifiPsdu> psdu,
2046 const RxSignalInfo& rxSignalInfo,
2047 const WifiTxVector& txVector,
2048 const std::vector<bool>& perMpduStatus)
2049{
2051 this << *psdu << rxSignalInfo << txVector << perMpduStatus.size()
2052 << std::all_of(perMpduStatus.begin(), perMpduStatus.end(), [](bool v) { return v; }));
2053
2054 std::set<uint8_t> tids = psdu->GetTids();
2055
2056 // Multi-TID A-MPDUs are not supported yet
2057 if (tids.size() == 1)
2058 {
2059 uint8_t tid = *tids.begin();
2060 WifiMacHeader::QosAckPolicy ackPolicy = psdu->GetAckPolicyForTid(tid);
2061 NS_ASSERT(psdu->GetNMpdus() > 1);
2062
2063 if (ackPolicy == WifiMacHeader::NORMAL_ACK)
2064 {
2065 // Normal Ack or Implicit Block Ack Request
2066 NS_LOG_DEBUG("Schedule Block Ack");
2067 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
2068 NS_ASSERT(agreement);
2069
2070 Simulator::Schedule(
2071 m_phy->GetSifs(),
2072 &HtFrameExchangeManager::SendBlockAck,
2073 this,
2074 *agreement,
2075 psdu->GetDuration(),
2076 GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
2077 rxSignalInfo.snr,
2078 std::nullopt);
2079 }
2080 else if (psdu->GetAddr1().IsGroup() && (ackPolicy == WifiMacHeader::NO_ACK))
2081 {
2082 // groupcast A-MPDU received
2083 m_flushGroupcastMpdusEvent.Cancel();
2084
2085 /*
2086 * There might be pending MPDUs from a previous groupcast transmission
2087 * that have not been forwarded up yet (e.g. all transmission attempts
2088 * of a given MPDU have failed). For groupcast transmissions using GCR-UR service,
2089 * transmitter keeps advancing its window since there is no feedback from the
2090 * recipients. In order to forward up previously received groupcast MPDUs and avoid
2091 * following MPDUs not to be forwarded up, we flush the recipient window. The sequence
2092 * number to use can easily be deduced since sequence number of groupcast MPDUs are
2093 * consecutive.
2094 */
2095 const auto startSeq = psdu->GetHeader(0).GetSequenceNumber();
2096 const auto groupAddress = psdu->GetHeader(0).IsQosAmsdu()
2097 ? (*psdu->begin())->begin()->second.GetDestinationAddr()
2098 : psdu->GetAddr1();
2099 FlushGroupcastMpdus(groupAddress, psdu->GetAddr2(), tid, startSeq);
2100
2101 /*
2102 * In case all MPDUs of all following transmissions are corrupted or
2103 * if no following groupcast transmission happens, some groupcast MPDUs
2104 * of the currently received A-MPDU would never be forwarded up. To prevent this,
2105 * we schedule a flush of the recipient window once the MSDU lifetime limit elapsed.
2106 */
2107 const auto stopSeq = (startSeq + perMpduStatus.size()) % 4096;
2108 const auto maxDelay = m_mac->GetQosTxop(tid)->GetWifiMacQueue()->GetMaxDelay();
2109 m_flushGroupcastMpdusEvent =
2110 Simulator::Schedule(maxDelay,
2111 &HtFrameExchangeManager::FlushGroupcastMpdus,
2112 this,
2113 groupAddress,
2114 psdu->GetAddr2(),
2115 tid,
2116 stopSeq);
2117 }
2118 }
2119}
2120
2121void
2122HtFrameExchangeManager::FlushGroupcastMpdus(const Mac48Address& groupAddress,
2123 const Mac48Address& originator,
2124 uint8_t tid,
2125 uint16_t seq)
2126{
2127 NS_LOG_FUNCTION(this << groupAddress << originator << tid << seq);
2128 // We can flush the recipient window by indicating the reception of an implicit GCR BAR
2129 GetBaManager(tid)->NotifyGotBlockAckRequest(originator, tid, seq, groupAddress);
2130}
2131
2132void
2133HtFrameExchangeManager::NotifyLastGcrUrTx(Ptr<const WifiMpdu> mpdu)
2134{
2135 NS_LOG_FUNCTION(this << mpdu);
2136 const auto tid = mpdu->GetHeader().GetQosTid();
2137 const auto groupAddress = mpdu->GetHeader().GetAddr1();
2138 if (!GetBaManager(tid)->IsGcrAgreementEstablished(
2139 groupAddress,
2140 tid,
2141 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(groupAddress)))
2142 {
2143 return;
2144 }
2145 GetBaManager(tid)->NotifyLastGcrUrTx(
2146 mpdu,
2147 m_apMac->GetGcrManager()->GetMemberStasForGroupAddress(groupAddress));
2148}
2149
2150} // namespace ns3
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)
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.
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 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.
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.
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:609
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:837
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition wifi-phy.cc:1563
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition wifi-phy.cc:1057
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition wifi-phy.cc:1556
std::set< uint8_t > GetTids() const
Get the set of TIDs of the QoS Data frames included in the PSDU.
Definition wifi-psdu.cc:173
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition wifi-psdu.cc:274
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition wifi-psdu.cc:328
uint32_t GetSize() const
Return the size of the PSDU in bytes.
Definition wifi-psdu.cc:268
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:187
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.
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:71
@ 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:273
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition wifi-utils.cc:61
uint32_t GetAckSize()
Return the total Ack size (including FCS trailer).
Definition wifi-utils.cc:53
static constexpr uint16_t SU_STA_ID
STA_ID to identify a single user (SU)
Definition wifi-mode.h:24
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:78
double snr
SNR in linear scale.
Definition wifi-types.h:79
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