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