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 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Stefano Avallone <stavallo@unina.it>
18 */
19
21
22#include "ht-configuration.h"
23
24#include "ns3/abort.h"
25#include "ns3/assert.h"
26#include "ns3/ctrl-headers.h"
27#include "ns3/log.h"
28#include "ns3/mgt-action-headers.h"
29#include "ns3/recipient-block-ack-agreement.h"
30#include "ns3/snr-tag.h"
31#include "ns3/sta-wifi-mac.h"
32#include "ns3/vht-configuration.h"
33#include "ns3/wifi-mac-queue.h"
34#include "ns3/wifi-net-device.h"
35#include "ns3/wifi-utils.h"
36
37#include <array>
38#include <optional>
39
40#undef NS_LOG_APPEND_CONTEXT
41#define NS_LOG_APPEND_CONTEXT WIFI_FEM_NS_LOG_APPEND_CONTEXT
42
43namespace ns3
44{
45
46NS_LOG_COMPONENT_DEFINE("HtFrameExchangeManager");
47
48NS_OBJECT_ENSURE_REGISTERED(HtFrameExchangeManager);
49
50TypeId
52{
53 static TypeId tid = TypeId("ns3::HtFrameExchangeManager")
55 .AddConstructor<HtFrameExchangeManager>()
56 .SetGroupName("Wifi");
57 return tid;
58}
59
61{
62 NS_LOG_FUNCTION(this);
63 m_msduAggregator = CreateObject<MsduAggregator>();
64 m_mpduAggregator = CreateObject<MpduAggregator>();
65}
66
68{
70}
71
72void
74{
75 NS_LOG_FUNCTION(this);
76 m_pendingAddBaResp.clear();
77 m_msduAggregator = nullptr;
78 m_mpduAggregator = nullptr;
79 m_psdu = nullptr;
82}
83
84void
86{
87 m_msduAggregator->SetWifiMac(mac);
88 m_mpduAggregator->SetWifiMac(mac);
90}
91
94{
95 return m_msduAggregator;
96}
97
100{
101 return m_mpduAggregator;
102}
103
106{
107 return m_mac->GetQosTxop(tid)->GetBaManager();
108}
109
110bool
112{
113 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
114 bool establish;
115
116 // NOLINTBEGIN(bugprone-branch-clone)
117 if (!m_mac->GetHtConfiguration() ||
118 (!GetWifiRemoteStationManager()->GetHtSupported(recipient) &&
119 !GetWifiRemoteStationManager()->GetStationHe6GhzCapabilities(recipient)))
120 {
121 // no Block Ack if this device or the recipient are not HT STAs and do not operate
122 // in the 6 GHz band
123 establish = false;
124 }
125 else if (auto agreement = qosTxop->GetBaManager()->GetAgreementAsOriginator(recipient, tid);
126 agreement && !agreement->get().IsReset())
127 {
128 // Block Ack agreement already established
129 establish = false;
130 }
131 // NOLINTEND(bugprone-branch-clone)
132 else
133 {
134 WifiContainerQueueId queueId{WIFI_QOSDATA_QUEUE, WIFI_UNICAST, recipient, tid};
135 uint32_t packets = qosTxop->GetWifiMacQueue()->GetNPackets(queueId);
136 establish =
138 (qosTxop->GetBlockAckThreshold() > 0 && packets >= qosTxop->GetBlockAckThreshold()) ||
139 (m_mpduAggregator->GetMaxAmpduSize(recipient, tid, WIFI_MOD_CLASS_HT) > 0 &&
140 packets > 1) ||
142 }
143
144 NS_LOG_FUNCTION(this << recipient << +tid << establish);
145 return establish;
146}
147
148bool
150 uint8_t tid,
151 uint16_t startingSeq,
152 uint16_t timeout,
153 bool immediateBAck,
154 Time availableTime)
155{
156 NS_LOG_FUNCTION(this << dest << +tid << startingSeq << timeout << immediateBAck
157 << availableTime);
158 NS_LOG_DEBUG("Send ADDBA request to " << dest);
159
160 WifiMacHeader hdr;
162 // use the remote link address if dest is an MLD address
163 auto addr1 = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(dest);
164 hdr.SetAddr1(addr1 ? *addr1 : dest);
165 hdr.SetAddr2(m_self);
166 hdr.SetAddr3(m_bssid);
167 hdr.SetDsNotTo();
168 hdr.SetDsNotFrom();
169
170 WifiActionHeader actionHdr;
173 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
174
175 Ptr<Packet> packet = Create<Packet>();
176 // Setting ADDBARequest header
178 reqHdr.SetAmsduSupport(true);
179 if (immediateBAck)
180 {
181 reqHdr.SetImmediateBlockAck();
182 }
183 else
184 {
185 reqHdr.SetDelayedBlockAck();
186 }
187 reqHdr.SetTid(tid);
188 /* For now we don't use buffer size field in the ADDBA request frame. The recipient
189 * will choose how many packets it can receive under block ack.
190 */
191 reqHdr.SetBufferSize(0);
192 reqHdr.SetTimeout(timeout);
193 // set the starting sequence number for the BA agreement
194 reqHdr.SetStartingSequence(startingSeq);
195
196 GetBaManager(tid)->CreateOriginatorAgreement(reqHdr, dest);
197
198 packet->AddHeader(reqHdr);
199 packet->AddHeader(actionHdr);
200
201 Ptr<WifiMpdu> mpdu = Create<WifiMpdu>(packet, hdr);
202
203 // get the sequence number for the ADDBA Request management frame
204 uint16_t sequence = m_txMiddle->GetNextSequenceNumberFor(&mpdu->GetHeader());
205 mpdu->GetHeader().SetSequenceNumber(sequence);
206
207 WifiTxParameters txParams;
208 txParams.m_txVector =
209 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
210 if (!TryAddMpdu(mpdu, txParams, availableTime))
211 {
212 NS_LOG_DEBUG("Not enough time to send the ADDBA Request frame");
213 return false;
214 }
215
216 // Wifi MAC queue scheduler is expected to prioritize management frames
217 m_mac->GetQosTxop(tid)->GetWifiMacQueue()->Enqueue(mpdu);
218 SendMpduWithProtection(mpdu, txParams);
219 return true;
220}
221
222void
224 Mac48Address originator)
225{
226 NS_LOG_FUNCTION(this << originator);
227 WifiMacHeader hdr;
229 hdr.SetAddr1(originator);
230 hdr.SetAddr2(m_self);
231 hdr.SetAddr3(m_bssid);
232 hdr.SetDsNotFrom();
233 hdr.SetDsNotTo();
234
236 StatusCode code;
237 code.SetSuccess();
238 respHdr.SetStatusCode(code);
239 // Here a control about queues type?
240 respHdr.SetAmsduSupport(reqHdr->IsAmsduSupported());
241
242 if (reqHdr->IsImmediateBlockAck())
243 {
244 respHdr.SetImmediateBlockAck();
245 }
246 else
247 {
248 respHdr.SetDelayedBlockAck();
249 }
250 auto tid = reqHdr->GetTid();
251 respHdr.SetTid(tid);
252
253 auto bufferSize = std::min(m_mac->GetMpduBufferSize(), m_mac->GetMaxBaBufferSize(originator));
254 respHdr.SetBufferSize(bufferSize);
255 respHdr.SetTimeout(reqHdr->GetTimeout());
256
257 WifiActionHeader actionHdr;
260 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
261
262 Ptr<Packet> packet = Create<Packet>();
263 packet->AddHeader(respHdr);
264 packet->AddHeader(actionHdr);
265
266 // Get the MLD address of the originator, if an ML setup was performed
267 if (auto originatorMld = GetWifiRemoteStationManager()->GetMldAddress(originator))
268 {
269 originator = *originatorMld;
270 }
271 GetBaManager(tid)->CreateRecipientAgreement(respHdr,
272 originator,
273 reqHdr->GetStartingSequence(),
274 m_rxMiddle);
275
276 auto agreement = GetBaManager(tid)->GetAgreementAsRecipient(originator, tid);
277 NS_ASSERT(agreement);
278 if (respHdr.GetTimeout() != 0)
279 {
280 Time timeout = MicroSeconds(1024 * agreement->get().GetTimeout());
281
282 agreement->get().m_inactivityEvent =
285 this,
286 originator,
287 tid,
288 false);
289 }
290
291 auto mpdu = Create<WifiMpdu>(packet, hdr);
292
293 /*
294 * It is possible (though, unlikely) that at this point there are other ADDBA_RESPONSE frame(s)
295 * in the MAC queue. This may happen if the recipient receives an ADDBA_REQUEST frame, enqueues
296 * an ADDBA_RESPONSE frame, but is not able to successfully transmit it before the timer to
297 * wait for ADDBA_RESPONSE expires at the originator. The latter may then send another
298 * ADDBA_REQUEST frame, which triggers the creation of another ADDBA_RESPONSE frame.
299 * To avoid sending unnecessary ADDBA_RESPONSE frames, we keep track of the previously enqueued
300 * ADDBA_RESPONSE frame (if any), dequeue it and replace it with the new ADDBA_RESPONSE frame.
301 */
302
303 // remove any pending ADDBA_RESPONSE frame
304 AgreementKey key(originator, tid);
305 if (auto it = m_pendingAddBaResp.find(key); it != m_pendingAddBaResp.end())
306 {
307 NS_ASSERT_MSG(it->second, "The pointer to the pending ADDBA_RESPONSE cannot be null");
308 DequeueMpdu(it->second);
309 m_pendingAddBaResp.erase(it);
310 }
311 // store the new ADDBA_RESPONSE frame
312 m_pendingAddBaResp[key] = mpdu;
313
314 // It is unclear which queue this frame should go into. For now we
315 // bung it into the queue corresponding to the TID for which we are
316 // establishing an agreement, and push it to the head.
317 // Wifi MAC queue scheduler is expected to prioritize management frames
318 m_mac->GetQosTxop(tid)->Queue(mpdu);
319}
320
321void
322HtFrameExchangeManager::SendDelbaFrame(Mac48Address addr, uint8_t tid, bool byOriginator)
323{
324 NS_LOG_FUNCTION(this << addr << +tid << byOriginator);
325 WifiMacHeader hdr;
327 // use the remote link address if addr is an MLD address
328 hdr.SetAddr1(GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr).value_or(addr));
329 hdr.SetAddr2(m_self);
330 hdr.SetAddr3(m_bssid);
331 hdr.SetDsNotTo();
332 hdr.SetDsNotFrom();
333
334 MgtDelBaHeader delbaHdr;
335 delbaHdr.SetTid(tid);
336 byOriginator ? delbaHdr.SetByOriginator() : delbaHdr.SetByRecipient();
337
338 WifiActionHeader actionHdr;
340 action.blockAck = WifiActionHeader::BLOCK_ACK_DELBA;
341 actionHdr.SetAction(WifiActionHeader::BLOCK_ACK, action);
342
343 Ptr<Packet> packet = Create<Packet>();
344 packet->AddHeader(delbaHdr);
345 packet->AddHeader(actionHdr);
346
347 m_mac->GetQosTxop(tid)->Queue(Create<WifiMpdu>(packet, hdr));
348}
349
350bool
351HtFrameExchangeManager::StartFrameExchange(Ptr<QosTxop> edca, Time availableTime, bool initialFrame)
352{
353 NS_LOG_FUNCTION(this << edca << availableTime << initialFrame);
354
355 // First, check if there is a BAR to be transmitted
356 if (auto mpdu = GetBar(edca->GetAccessCategory());
357 mpdu && SendMpduFromBaManager(mpdu, availableTime, initialFrame))
358 {
359 return true;
360 }
361
362 Ptr<WifiMpdu> peekedItem = edca->PeekNextMpdu(m_linkId);
363
364 // Even though channel access is requested when the queue is not empty, at
365 // the time channel access is granted the lifetime of the packet might be
366 // expired and the queue might be empty.
367 if (!peekedItem)
368 {
369 NS_LOG_DEBUG("No frames available for transmission");
370 return false;
371 }
372
373 const WifiMacHeader& hdr = peekedItem->GetHeader();
374 // setup a Block Ack agreement if needed
375 if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() &&
377 {
378 // if the peeked MPDU has been already transmitted, use its sequence number
379 // as the starting sequence number for the BA agreement, otherwise use the
380 // next available sequence number
381 uint16_t startingSeq =
382 (hdr.IsRetry()
383 ? hdr.GetSequenceNumber()
384 : m_txMiddle->GetNextSeqNumberByTidAndAddress(hdr.GetQosTid(), hdr.GetAddr1()));
385 return SendAddBaRequest(hdr.GetAddr1(),
386 hdr.GetQosTid(),
387 startingSeq,
388 edca->GetBlockAckInactivityTimeout(),
389 true,
390 availableTime);
391 }
392
393 // Use SendDataFrame if we can try aggregation
394 if (hdr.IsQosData() && !hdr.GetAddr1().IsGroup() && !peekedItem->IsFragment() &&
395 !GetWifiRemoteStationManager()->NeedFragmentation(peekedItem =
396 CreateAliasIfNeeded(peekedItem)))
397 {
398 return SendDataFrame(peekedItem, availableTime, initialFrame);
399 }
400
401 // Use the QoS FEM to transmit the frame in all the other cases, i.e.:
402 // - the frame is not a QoS data frame
403 // - the frame is a broadcast QoS data frame
404 // - the frame is a fragment
405 // - the frame must be fragmented
406 return QosFrameExchangeManager::StartFrameExchange(edca, availableTime, initialFrame);
407}
408
411 std::optional<uint8_t> optTid,
412 std::optional<Mac48Address> optAddress)
413{
414 NS_LOG_FUNCTION(this << +ac << optTid.has_value() << optAddress.has_value());
415 NS_ASSERT_MSG(optTid.has_value() == optAddress.has_value(),
416 "Either both or none of TID and address must be provided");
417
418 // remove all expired MPDUs from the MAC queue, so that
419 // BlockAckRequest frames (if needed) are scheduled
420 auto queue = m_mac->GetTxopQueue(ac);
421 queue->WipeAllExpiredMpdus();
422
423 Ptr<WifiMpdu> bar;
424 Ptr<WifiMpdu> prevBar;
425 Ptr<WifiMpdu> selectedBar;
426
427 // we could iterate over all the scheduler's queues and ignore those that do not contain
428 // control frames, but it's more efficient to peek frames until we get frames that are
429 // not control frames, given that control frames have the highest priority
430 while ((bar = queue->PeekFirstAvailable(m_linkId, prevBar)) && bar && bar->GetHeader().IsCtl())
431 {
432 if (bar->GetHeader().IsBlockAckReq())
433 {
435 bar->GetPacket()->PeekHeader(reqHdr);
436 auto tid = reqHdr.GetTidInfo();
437 Mac48Address recipient = bar->GetHeader().GetAddr1();
438 auto recipientMld = m_mac->GetMldAddress(recipient);
439
440 // the scheduler should not return a BlockAckReq that cannot be sent on this link:
441 // either the TA address is the address of this link or it is the MLD address and
442 // the RA field is the MLD address of a device we can communicate with on this link
443 NS_ASSERT_MSG(bar->GetHeader().GetAddr2() == m_self ||
444 (bar->GetHeader().GetAddr2() == m_mac->GetAddress() && recipientMld &&
445 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(recipient)),
446 "Cannot use link " << +m_linkId << " to send BAR: " << *bar);
447
448 if (optAddress &&
449 (GetWifiRemoteStationManager()->GetMldAddress(*optAddress).value_or(*optAddress) !=
450 GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient) ||
451 optTid != tid))
452 {
453 NS_LOG_DEBUG("BAR " << *bar
454 << " cannot be returned because it is not addressed"
455 " to the given station for the given TID");
456 prevBar = bar;
457 continue;
458 }
459
460 auto agreement = m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid);
461 if (!agreement)
462 {
463 NS_LOG_DEBUG("BA agreement with " << recipient << " for TID=" << +tid
464 << " was torn down");
465 queue->Remove(bar);
466 continue;
467 }
468 // update BAR if the starting sequence number changed
469 if (auto seqNo = agreement->get().GetStartingSequence();
470 reqHdr.GetStartingSequence() != seqNo)
471 {
472 reqHdr.SetStartingSequence(seqNo);
473 Ptr<Packet> packet = Create<Packet>();
474 packet->AddHeader(reqHdr);
475 auto updatedBar = Create<WifiMpdu>(packet, bar->GetHeader(), bar->GetTimestamp());
476 queue->Replace(bar, updatedBar);
477 bar = updatedBar;
478 }
479 // bar is the BlockAckReq to send
480 selectedBar = bar;
481
482 // if the selected BAR is intended to be sent on this specific link and the recipient
483 // is an MLD, remove the BAR (if any) for this BA agreement that can be sent on any
484 // link (because a BAR that can be sent on any link to a recipient is no longer
485 // needed after sending a BAR to that recipient on this link)
486 if (bar->GetHeader().GetAddr2() == m_self && recipientMld)
487 {
490 *recipientMld,
491 std::nullopt};
492 Ptr<WifiMpdu> otherBar;
493 while ((otherBar = queue->PeekByQueueId(queueId, otherBar)))
494 {
495 if (otherBar->GetHeader().IsBlockAckReq())
496 {
497 CtrlBAckRequestHeader otherReqHdr;
498 otherBar->GetPacket()->PeekHeader(otherReqHdr);
499 if (otherReqHdr.GetTidInfo() == tid)
500 {
501 queue->Remove(otherBar);
502 break;
503 }
504 }
505 }
506 }
507 break;
508 }
509 if (bar->GetHeader().IsTrigger() && !optAddress && !selectedBar)
510 {
511 return bar;
512 }
513 // not a BAR nor a Trigger Frame, continue
514 prevBar = bar;
515 }
516
517 if (!selectedBar)
518 {
519 // check if we can send a BAR to a recipient to which a BAR can only be sent if data queued
520 auto baManager = m_mac->GetQosTxop(ac)->GetBaManager();
521 for (const auto& [recipient, tid] : baManager->GetSendBarIfDataQueuedList())
522 {
523 if (queue->PeekByTidAndAddress(tid, recipient))
524 {
525 auto [reqHdr, hdr] = m_mac->GetQosTxop(ac)->PrepareBlockAckRequest(recipient, tid);
526 auto pkt = Create<Packet>();
527 pkt->AddHeader(reqHdr);
528 selectedBar = Create<WifiMpdu>(pkt, hdr);
529 baManager->RemoveFromSendBarIfDataQueuedList(recipient, tid);
530 queue->Enqueue(selectedBar);
531 break;
532 }
533 }
534 }
535
536 if (selectedBar && selectedBar->GetHeader().GetAddr2() != m_self)
537 {
538 // the selected BAR has MLD addresses in Addr1/Addr2, replace them with link addresses
539 // and move to the appropriate container queue
540 NS_ASSERT(selectedBar->GetHeader().GetAddr2() == m_mac->GetAddress());
541 DequeueMpdu(selectedBar);
542 const auto currAddr1 = selectedBar->GetHeader().GetAddr1();
543 auto addr1 =
544 GetWifiRemoteStationManager()->GetAffiliatedStaAddress(currAddr1).value_or(currAddr1);
545 selectedBar->GetHeader().SetAddr1(addr1);
546 selectedBar->GetHeader().SetAddr2(m_self);
547 queue->Enqueue(selectedBar);
548 }
549
550 return selectedBar;
551}
552
553bool
555 Time availableTime,
556 bool initialFrame)
557{
558 NS_LOG_FUNCTION(this << *mpdu << availableTime << initialFrame);
559
560 // First, check if there is a BAR to be transmitted
561 if (!mpdu->GetHeader().IsBlockAckReq())
562 {
563 NS_LOG_DEBUG("Block Ack Manager returned no frame to send");
564 return false;
565 }
566
567 // Prepare the TX parameters. Note that the default ack manager expects the
568 // data TxVector in the m_txVector field to compute the BlockAck TxVector.
569 // The m_txVector field of the TX parameters is set to the BlockAckReq TxVector
570 // a few lines below.
571 WifiTxParameters txParams;
572 txParams.m_txVector =
573 GetWifiRemoteStationManager()->GetDataTxVector(mpdu->GetHeader(), m_allowedWidth);
574
575 if (!TryAddMpdu(mpdu, txParams, availableTime))
576 {
577 NS_LOG_DEBUG("Not enough time to send the BAR frame returned by the Block Ack Manager");
578 return false;
579 }
580
582
583 // the BlockAckReq frame is sent using the same TXVECTOR as the BlockAck frame
584 auto blockAcknowledgment = static_cast<WifiBlockAck*>(txParams.m_acknowledgment.get());
585 txParams.m_txVector = blockAcknowledgment->blockAckTxVector;
586
587 // we can transmit the BlockAckReq frame
588 SendPsduWithProtection(GetWifiPsdu(mpdu, txParams.m_txVector), txParams);
589 return true;
590}
591
592bool
594 Time availableTime,
595 bool initialFrame)
596{
597 NS_ASSERT(peekedItem && peekedItem->GetHeader().IsQosData() &&
598 !peekedItem->GetHeader().GetAddr1().IsBroadcast() && !peekedItem->IsFragment());
599 NS_LOG_FUNCTION(this << *peekedItem << availableTime << initialFrame);
600
601 Ptr<QosTxop> edca = m_mac->GetQosTxop(peekedItem->GetHeader().GetQosTid());
602 WifiTxParameters txParams;
603 txParams.m_txVector =
604 GetWifiRemoteStationManager()->GetDataTxVector(peekedItem->GetHeader(), m_allowedWidth);
605 Ptr<WifiMpdu> mpdu =
606 edca->GetNextMpdu(m_linkId, peekedItem, txParams, availableTime, initialFrame);
607
608 if (!mpdu)
609 {
610 NS_LOG_DEBUG("Not enough time to transmit a frame");
611 return false;
612 }
613
614 // try A-MPDU aggregation
615 std::vector<Ptr<WifiMpdu>> mpduList =
616 m_mpduAggregator->GetNextAmpdu(mpdu, txParams, availableTime);
617 NS_ASSERT(txParams.m_acknowledgment);
618
619 if (mpduList.size() > 1)
620 {
621 // A-MPDU aggregation succeeded
622 SendPsduWithProtection(Create<WifiPsdu>(std::move(mpduList)), txParams);
623 }
624 else if (txParams.m_acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
625 {
626 // a QoS data frame using the Block Ack policy can be followed by a BlockAckReq
627 // frame and a BlockAck frame. Such a sequence is handled by the HT FEM
628 SendPsduWithProtection(Create<WifiPsdu>(mpdu, false), txParams);
629 }
630 else
631 {
632 // transmission can be handled by the base FEM
633 SendMpduWithProtection(mpdu, txParams);
634 }
635
636 return true;
637}
638
639void
641{
642 NS_LOG_FUNCTION(this << acknowledgment);
643 NS_ASSERT(acknowledgment);
644
645 if (acknowledgment->method == WifiAcknowledgment::BLOCK_ACK)
646 {
647 auto blockAcknowledgment = static_cast<WifiBlockAck*>(acknowledgment);
648 Time baTxDuration = m_phy->CalculateTxDuration(GetBlockAckSize(blockAcknowledgment->baType),
649 blockAcknowledgment->blockAckTxVector,
650 m_phy->GetPhyBand());
651 blockAcknowledgment->acknowledgmentTime = m_phy->GetSifs() + baTxDuration;
652 }
653 else if (acknowledgment->method == WifiAcknowledgment::BAR_BLOCK_ACK)
654 {
655 auto barBlockAcknowledgment = static_cast<WifiBarBlockAck*>(acknowledgment);
656 Time barTxDuration =
657 m_phy->CalculateTxDuration(GetBlockAckRequestSize(barBlockAcknowledgment->barType),
658 barBlockAcknowledgment->blockAckReqTxVector,
659 m_phy->GetPhyBand());
660 Time baTxDuration =
661 m_phy->CalculateTxDuration(GetBlockAckSize(barBlockAcknowledgment->baType),
662 barBlockAcknowledgment->blockAckTxVector,
663 m_phy->GetPhyBand());
664 barBlockAcknowledgment->acknowledgmentTime =
665 2 * m_phy->GetSifs() + barTxDuration + baTxDuration;
666 }
667 else
668 {
670 }
671}
672
673void
675{
676 ForwardPsduDown(GetWifiPsdu(mpdu, txVector), txVector);
677}
678
681{
682 return Create<WifiPsdu>(mpdu, false);
683}
684
685void
687{
688 NS_LOG_FUNCTION(this << *mpdu);
689
690 if (mpdu->GetHeader().IsQosData())
691 {
692 uint8_t tid = mpdu->GetHeader().GetQosTid();
693 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
694
695 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
696 {
697 // notify the BA manager that the MPDU was acknowledged
698 edca->GetBaManager()->NotifyGotAck(m_linkId, mpdu);
699 }
700 }
701 else if (mpdu->GetHeader().IsAction())
702 {
703 auto addr1 = mpdu->GetHeader().GetAddr1();
704 auto address = GetWifiRemoteStationManager()->GetMldAddress(addr1).value_or(addr1);
705 WifiActionHeader actionHdr;
706 Ptr<Packet> p = mpdu->GetPacket()->Copy();
707 p->RemoveHeader(actionHdr);
708 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK)
709 {
711 {
712 MgtDelBaHeader delBa;
713 p->PeekHeader(delBa);
714 auto tid = delBa.GetTid();
715 if (delBa.IsByOriginator())
716 {
717 GetBaManager(tid)->DestroyOriginatorAgreement(address, tid);
718 }
719 else
720 {
721 GetBaManager(tid)->DestroyRecipientAgreement(address, tid);
722 }
723 }
725 {
726 // Setup ADDBA response timeout
728 p->PeekHeader(addBa);
729 Ptr<QosTxop> edca = m_mac->GetQosTxop(addBa.GetTid());
730 Simulator::Schedule(edca->GetAddBaResponseTimeout(),
732 edca,
733 address,
734 addBa.GetTid());
735 }
737 {
738 // A recipient Block Ack agreement must exist
740 p->PeekHeader(addBa);
741 auto tid = addBa.GetTid();
742 NS_ASSERT_MSG(GetBaManager(tid)->GetAgreementAsRecipient(address, tid),
743 "Recipient BA agreement {" << address << ", " << +tid
744 << "} not found");
745 m_pendingAddBaResp.erase({address, tid});
746 }
747 }
748 }
750}
751
752void
754{
755 NS_LOG_DEBUG(this);
756
758 {
759 // A TXOP limit of 0 indicates that the TXOP holder may transmit or cause to
760 // be transmitted (as responses) the following within the current TXOP:
761 // f) Any number of BlockAckReq frames
762 // (Sec. 10.22.2.8 of 802.11-2016)
763 NS_LOG_DEBUG("Schedule a transmission from Block Ack Manager in a SIFS");
766
767 // TXOP limit is null, hence the txopDuration parameter is unused
769 }
770 else
771 {
773 }
774}
775
776void
778{
779 NS_LOG_FUNCTION(this << *mpdu);
780
781 if (mpdu->GetHeader().IsQosData())
782 {
783 GetBaManager(mpdu->GetHeader().GetQosTid())->NotifyDiscardedMpdu(mpdu);
784 }
785 else if (mpdu->GetHeader().IsAction())
786 {
787 WifiActionHeader actionHdr;
788 mpdu->GetPacket()->PeekHeader(actionHdr);
789 if (actionHdr.GetCategory() == WifiActionHeader::BLOCK_ACK &&
791 {
792 uint8_t tid = GetTid(mpdu->GetPacket(), mpdu->GetHeader());
793 auto recipient = mpdu->GetHeader().GetAddr1();
794 // if the recipient is an MLD, use its MLD address
795 if (auto mldAddr = GetWifiRemoteStationManager()->GetMldAddress(recipient))
796 {
797 recipient = *mldAddr;
798 }
799 if (auto agreement = GetBaManager(tid)->GetAgreementAsOriginator(recipient, tid);
800 agreement && agreement->get().IsPending())
801 {
802 NS_LOG_DEBUG("No ACK after ADDBA request");
803 Ptr<QosTxop> qosTxop = m_mac->GetQosTxop(tid);
804 qosTxop->NotifyOriginatorAgreementNoReply(recipient, tid);
805 Simulator::Schedule(qosTxop->GetFailedAddBaTimeout(),
807 qosTxop,
808 recipient,
809 tid);
810 }
811 }
812 }
814}
815
816void
818{
819 NS_LOG_FUNCTION(this << *mpdu);
820
821 if (mpdu->GetHeader().IsQosData())
822 {
823 uint8_t tid = mpdu->GetHeader().GetQosTid();
824 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
825
826 if (m_mac->GetBaAgreementEstablishedAsOriginator(mpdu->GetHeader().GetAddr1(), tid))
827 {
828 // notify the BA manager that the MPDU was not acknowledged
829 edca->GetBaManager()->NotifyMissedAck(m_linkId, mpdu);
830 return;
831 }
832 }
834}
835
836void
838{
839 NS_LOG_FUNCTION(this << *psdu);
840
841 auto tids = psdu->GetTids();
842
843 if (tids.empty() || // no QoS data frames included
844 !m_mac->GetBaAgreementEstablishedAsOriginator(psdu->GetAddr1(), *tids.begin()))
845 {
847 return;
848 }
849
850 // iterate over MPDUs in reverse order (to process them in decreasing order of sequence number)
851 auto mpduIt = psdu->end();
852
853 do
854 {
855 std::advance(mpduIt, -1);
856
857 const WifiMacHeader& hdr = (*mpduIt)->GetOriginal()->GetHeader();
858 if (hdr.IsQosData())
859 {
860 uint8_t tid = hdr.GetQosTid();
862
863 if (!hdr.IsRetry() && !(*mpduIt)->IsInFlight())
864 {
865 // The MPDU has never been transmitted, so we can make its sequence
866 // number available again if it is the highest sequence number
867 // assigned by the MAC TX middle
868 uint16_t currentNextSeq = m_txMiddle->PeekNextSequenceNumberFor(&hdr);
869
870 if ((hdr.GetSequenceNumber() + 1) % SEQNO_SPACE_SIZE == currentNextSeq)
871 {
872 (*mpduIt)->UnassignSeqNo();
873 m_txMiddle->SetSequenceNumberFor(&hdr);
874
875 NS_LOG_DEBUG("Released " << hdr.GetSequenceNumber()
876 << ", next sequence "
877 "number for dest="
878 << hdr.GetAddr1() << ",tid=" << +tid << " is "
879 << m_txMiddle->PeekNextSequenceNumberFor(&hdr));
880 }
881 }
882 }
883 } while (mpduIt != psdu->begin());
884}
885
886Time
888{
889 NS_LOG_FUNCTION(this << txDuration << &txParams);
890
892
894 {
895 NS_ASSERT(txParams.m_acknowledgment &&
896 txParams.m_acknowledgment->acknowledgmentTime.has_value());
897 return *txParams.m_acknowledgment->acknowledgmentTime;
898 }
899
900 // under multiple protection settings, if the TXOP limit is not null, Duration/ID
901 // is set to cover the remaining TXOP time (Sec. 9.2.5.2 of 802.11-2016).
902 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8
903 // of 802.11-2016)
904 return std::max(m_edca->GetRemainingTxop(m_linkId) - txDuration, Seconds(0));
905}
906
907void
909{
910 NS_LOG_FUNCTION(this << psdu << &txParams);
911
912 m_psdu = psdu;
913 m_txParams = std::move(txParams);
914
915#ifdef NS3_BUILD_PROFILE_DEBUG
916 // If protection is required, the MPDUs must be stored in some queue because
917 // they are not put back in a queue if the RTS/CTS exchange fails
919 {
920 for (const auto& mpdu : *PeekPointer(m_psdu))
921 {
922 NS_ASSERT(mpdu->GetHeader().IsCtl() || mpdu->IsQueued());
923 }
924 }
925#endif
926
927 // Make sure that the acknowledgment time has been computed, so that SendRts()
928 // and SendCtsToSelf() can reuse this value.
930
931 if (!m_txParams.m_acknowledgment->acknowledgmentTime.has_value())
932 {
934 }
935
936 // Set QoS Ack policy
938
939 for (const auto& mpdu : *PeekPointer(m_psdu))
940 {
941 if (mpdu->IsQueued())
942 {
943 mpdu->SetInFlight(m_linkId);
944 }
945 }
946
948}
949
950void
952{
953 NS_LOG_FUNCTION(this);
954 if (m_psdu)
955 {
957 m_sentRtsTo.clear();
958 SendPsdu();
959 return;
960 }
962}
963
964void
966{
967 NS_LOG_FUNCTION(this << *rts << txVector);
968
969 if (!m_psdu)
970 {
971 // A CTS Timeout occurred when protecting a single MPDU is handled by the
972 // parent classes
974 return;
975 }
976
978 m_psdu = nullptr;
979}
980
981void
983{
984 NS_LOG_FUNCTION(this);
985
986 Time txDuration =
988
990
992 {
994
995 std::set<uint8_t> tids = m_psdu->GetTids();
996 NS_ASSERT_MSG(tids.size() <= 1, "Multi-TID A-MPDUs are not supported");
997
998 if (tids.empty() || m_psdu->GetAckPolicyForTid(*tids.begin()) == WifiMacHeader::NO_ACK)
999 {
1000 // No acknowledgment, hence dequeue the PSDU if it is stored in a queue
1002 }
1003 }
1005 {
1007
1008 // the timeout duration is "aSIFSTime + aSlotTime + aRxPHYStartDelay, starting
1009 // at the PHY-TXEND.confirm primitive" (section 10.3.2.9 or 10.22.2.2 of 802.11-2016).
1010 // aRxPHYStartDelay equals the time to transmit the PHY header.
1011 auto blockAcknowledgment = static_cast<WifiBlockAck*>(m_txParams.m_acknowledgment.get());
1012
1013 Time timeout =
1014 txDuration + m_phy->GetSifs() + m_phy->GetSlot() +
1015 m_phy->CalculatePhyPreambleAndHeaderDuration(blockAcknowledgment->blockAckTxVector);
1018 timeout,
1019 {m_psdu->GetAddr1()},
1021 this,
1022 m_psdu,
1025 }
1027 {
1029
1030 // schedule the transmission of a BAR in a SIFS
1031 std::set<uint8_t> tids = m_psdu->GetTids();
1032 NS_ABORT_MSG_IF(tids.size() > 1,
1033 "Acknowledgment method incompatible with a Multi-TID A-MPDU");
1034 uint8_t tid = *tids.begin();
1035
1036 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1037 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(m_psdu->GetAddr1(), tid);
1038 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1039
1041 }
1042 else
1043 {
1044 NS_ABORT_MSG("Unable to handle the selected acknowledgment method ("
1045 << m_txParams.m_acknowledgment.get() << ")");
1046 }
1047
1048 // transmit the PSDU
1049 if (m_psdu->GetNMpdus() > 1)
1050 {
1052 }
1053 else
1054 {
1056 }
1057
1059 {
1060 // we are done in case the A-MPDU does not require acknowledgment
1061 m_psdu = nullptr;
1062 }
1063}
1064
1065void
1067{
1068 NS_LOG_FUNCTION(this << psdu);
1069
1070 for (const auto& mpdu : *PeekPointer(psdu))
1071 {
1072 auto& hdr = mpdu->GetHeader();
1073
1074 if (hdr.IsQosData() && hdr.HasData())
1075 {
1076 auto tid = hdr.GetQosTid();
1077 m_mac->GetQosTxop(tid)->CompleteMpduTx(mpdu);
1078 }
1079 }
1080}
1081
1082void
1084{
1085 NS_LOG_FUNCTION(this << psdu);
1086
1087 // use an array to avoid computing the queue size for every MPDU in the PSDU
1088 std::array<std::optional<uint8_t>, 8> queueSizeForTid;
1089
1090 for (const auto& mpdu : *PeekPointer(psdu))
1091 {
1092 WifiMacHeader& hdr = mpdu->GetHeader();
1093
1094 if (hdr.IsQosData())
1095 {
1096 uint8_t tid = hdr.GetQosTid();
1097 auto edca = m_mac->GetQosTxop(tid);
1098
1099 if (m_mac->GetTypeOfStation() == STA && (m_setQosQueueSize || hdr.IsQosEosp()))
1100 {
1101 // set the Queue Size subfield of the QoS Control field
1102 if (!queueSizeForTid[tid].has_value())
1103 {
1104 queueSizeForTid[tid] = edca->GetQosQueueSize(tid, hdr.GetAddr1());
1105 }
1106
1107 hdr.SetQosEosp();
1108 hdr.SetQosQueueSize(queueSizeForTid[tid].value());
1109 }
1110 }
1111 }
1112
1114}
1115
1116void
1118{
1119 NS_LOG_DEBUG(this << psdu);
1120
1121 for (const auto& mpdu : *PeekPointer(psdu))
1122 {
1123 DequeueMpdu(mpdu);
1124 }
1125}
1126
1127void
1129{
1130 NS_LOG_FUNCTION(this << psdu << txVector);
1131
1132 NS_LOG_DEBUG("Transmitting a PSDU: " << *psdu << " TXVECTOR: " << txVector);
1133 FinalizeMacHeader(psdu);
1134 NotifyTxToEdca(psdu);
1135 m_allowedWidth = std::min(m_allowedWidth, txVector.GetChannelWidth());
1136
1137 if (psdu->IsAggregate())
1138 {
1139 txVector.SetAggregation(true);
1140 }
1141
1142 m_phy->Send(psdu, txVector);
1143}
1144
1145bool
1147 const WifiTxParameters& txParams,
1148 Time ppduDurationLimit) const
1149{
1150 NS_ASSERT(mpdu);
1151 NS_LOG_FUNCTION(this << *mpdu << &txParams << ppduDurationLimit);
1152
1153 Mac48Address receiver = mpdu->GetHeader().GetAddr1();
1154 uint32_t ampduSize = txParams.GetSize(receiver);
1155
1156 if (!txParams.LastAddedIsFirstMpdu(receiver))
1157 {
1158 // we are attempting to perform A-MPDU aggregation, hence we have to check
1159 // that we meet the limit on the max A-MPDU size
1160 uint8_t tid;
1161 const WifiTxParameters::PsduInfo* info;
1162
1163 if (mpdu->GetHeader().IsQosData())
1164 {
1165 tid = mpdu->GetHeader().GetQosTid();
1166 }
1167 else if ((info = txParams.GetPsduInfo(receiver)) && !info->seqNumbers.empty())
1168 {
1169 tid = info->seqNumbers.begin()->first;
1170 }
1171 else
1172 {
1173 NS_ABORT_MSG("Cannot aggregate a non-QoS data frame to an A-MPDU that does"
1174 " not contain any QoS data frame");
1175 }
1176
1177 WifiModulationClass modulation = txParams.m_txVector.GetModulationClass();
1178
1179 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1180 {
1181 return false;
1182 }
1183 }
1184
1185 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1186}
1187
1188bool
1190 Mac48Address receiver,
1191 uint8_t tid,
1192 WifiModulationClass modulation) const
1193{
1194 NS_LOG_FUNCTION(this << ampduSize << receiver << +tid << modulation);
1195
1196 uint32_t maxAmpduSize = m_mpduAggregator->GetMaxAmpduSize(receiver, tid, modulation);
1197
1198 if (maxAmpduSize == 0)
1199 {
1200 NS_LOG_DEBUG("A-MPDU aggregation disabled");
1201 return false;
1202 }
1203
1204 if (ampduSize > maxAmpduSize)
1205 {
1206 NS_LOG_DEBUG("the frame does not meet the constraint on max A-MPDU size (" << maxAmpduSize
1207 << ")");
1208 return false;
1209 }
1210 return true;
1211}
1212
1213bool
1215 WifiTxParameters& txParams,
1216 Time availableTime) const
1217{
1218 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1219 NS_LOG_FUNCTION(this << *msdu << &txParams << availableTime);
1220
1221 // tentatively aggregate the given MPDU
1222 auto prevTxDuration = txParams.m_txDuration;
1223 txParams.AggregateMsdu(msdu);
1224 UpdateTxDuration(msdu->GetHeader().GetAddr1(), txParams);
1225
1226 // check if aggregating the given MSDU requires a different protection method
1227 NS_ASSERT(txParams.m_protection);
1228 auto protectionTime = txParams.m_protection->protectionTime;
1229
1230 std::unique_ptr<WifiProtection> protection;
1231 protection = GetProtectionManager()->TryAggregateMsdu(msdu, txParams);
1232 bool protectionSwapped = false;
1233
1234 if (protection)
1235 {
1236 // the protection method has changed, calculate the new protection time
1237 CalculateProtectionTime(protection.get());
1238 protectionTime = protection->protectionTime;
1239 // swap unique pointers, so that the txParams that is passed to the next
1240 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1241 txParams.m_protection.swap(protection);
1242 protectionSwapped = true;
1243 }
1244 NS_ASSERT(protectionTime.has_value());
1245
1246 // check if aggregating the given MSDU requires a different acknowledgment method
1247 NS_ASSERT(txParams.m_acknowledgment);
1248 auto acknowledgmentTime = txParams.m_acknowledgment->acknowledgmentTime;
1249
1250 std::unique_ptr<WifiAcknowledgment> acknowledgment;
1251 acknowledgment = GetAckManager()->TryAggregateMsdu(msdu, txParams);
1252 bool acknowledgmentSwapped = false;
1253
1254 if (acknowledgment)
1255 {
1256 // the acknowledgment method has changed, calculate the new acknowledgment time
1257 CalculateAcknowledgmentTime(acknowledgment.get());
1258 acknowledgmentTime = acknowledgment->acknowledgmentTime;
1259 // swap unique pointers, so that the txParams that is passed to the next
1260 // call to IsWithinLimitsIfAggregateMsdu is the most updated one
1261 txParams.m_acknowledgment.swap(acknowledgment);
1262 acknowledgmentSwapped = true;
1263 }
1264 NS_ASSERT(acknowledgmentTime.has_value());
1265
1266 Time ppduDurationLimit = Time::Min();
1267 if (availableTime != Time::Min())
1268 {
1269 ppduDurationLimit = availableTime - *protectionTime - *acknowledgmentTime;
1270 }
1271
1272 if (!IsWithinLimitsIfAggregateMsdu(msdu, txParams, ppduDurationLimit))
1273 {
1274 // adding MPDU failed, undo the addition of the MPDU and restore protection and
1275 // acknowledgment methods if they were swapped
1276 txParams.UndoAddMpdu();
1277 txParams.m_txDuration = prevTxDuration;
1278 if (protectionSwapped)
1279 {
1280 txParams.m_protection.swap(protection);
1281 }
1282 if (acknowledgmentSwapped)
1283 {
1284 txParams.m_acknowledgment.swap(acknowledgment);
1285 }
1286 return false;
1287 }
1288
1289 return true;
1290}
1291
1292bool
1294 const WifiTxParameters& txParams,
1295 Time ppduDurationLimit) const
1296{
1297 NS_ASSERT(msdu && msdu->GetHeader().IsQosData());
1298 NS_LOG_FUNCTION(this << *msdu << &txParams << ppduDurationLimit);
1299
1300 auto receiver = msdu->GetHeader().GetAddr1();
1301 auto tid = msdu->GetHeader().GetQosTid();
1302 auto modulation = txParams.m_txVector.GetModulationClass();
1303 auto psduInfo = txParams.GetPsduInfo(receiver);
1304 NS_ASSERT_MSG(psduInfo, "No PSDU info for receiver " << receiver);
1305
1306 // Check that the limit on A-MSDU size is met
1307 uint16_t maxAmsduSize = m_msduAggregator->GetMaxAmsduSize(receiver, tid, modulation);
1308
1309 if (maxAmsduSize == 0)
1310 {
1311 NS_LOG_DEBUG("A-MSDU aggregation disabled");
1312 return false;
1313 }
1314
1315 if (psduInfo->amsduSize > maxAmsduSize)
1316 {
1317 NS_LOG_DEBUG("No other MSDU can be aggregated: maximum A-MSDU size (" << maxAmsduSize
1318 << ") reached ");
1319 return false;
1320 }
1321
1322 const WifiTxParameters::PsduInfo* info = txParams.GetPsduInfo(msdu->GetHeader().GetAddr1());
1323 NS_ASSERT(info);
1324 auto ampduSize = txParams.GetSize(receiver);
1325
1326 if (info->ampduSize > 0)
1327 {
1328 // the A-MSDU being built is aggregated to other MPDUs in an A-MPDU.
1329 // Check that the limit on A-MPDU size is met.
1330 if (!IsWithinAmpduSizeLimit(ampduSize, receiver, tid, modulation))
1331 {
1332 return false;
1333 }
1334 }
1335
1336 return IsWithinSizeAndTimeLimits(ampduSize, receiver, txParams, ppduDurationLimit);
1337}
1338
1339void
1341{
1342 NS_LOG_FUNCTION(this << *psdu << txVector);
1343
1344 GetWifiRemoteStationManager()->ReportDataFailed(*psdu->begin());
1345
1346 bool resetCw;
1347 MissedBlockAck(psdu, txVector, resetCw);
1348
1350
1351 if (resetCw)
1352 {
1354 }
1355 else
1356 {
1358 }
1359
1360 m_psdu = nullptr;
1362}
1363
1364void
1366 const WifiTxVector& txVector,
1367 bool& resetCw)
1368{
1369 NS_LOG_FUNCTION(this << psdu << txVector << resetCw);
1370
1371 auto recipient = psdu->GetAddr1();
1372 auto recipientMld = GetWifiRemoteStationManager()->GetMldAddress(recipient).value_or(recipient);
1373 bool isBar;
1374 uint8_t tid;
1375
1376 if (psdu->GetNMpdus() == 1 && psdu->GetHeader(0).IsBlockAckReq())
1377 {
1378 isBar = true;
1379 CtrlBAckRequestHeader baReqHdr;
1380 psdu->GetPayload(0)->PeekHeader(baReqHdr);
1381 tid = baReqHdr.GetTidInfo();
1382 }
1383 else
1384 {
1385 isBar = false;
1386 std::set<uint8_t> tids = psdu->GetTids();
1387 NS_ABORT_MSG_IF(tids.size() > 1, "Multi-TID A-MPDUs not handled here");
1388 NS_ASSERT(!tids.empty());
1389 tid = *tids.begin();
1390 }
1391
1392 Ptr<QosTxop> edca = m_mac->GetQosTxop(tid);
1393
1394 if (edca->UseExplicitBarAfterMissedBlockAck() || isBar)
1395 {
1396 // we have to send a BlockAckReq, if needed
1397 if (GetBaManager(tid)->NeedBarRetransmission(tid, recipientMld))
1398 {
1399 NS_LOG_DEBUG("Missed Block Ack, transmit a BlockAckReq");
1400 /**
1401 * The BlockAckReq must be sent on the same link as the data frames to avoid issues.
1402 * As an example, assume that an A-MPDU is sent on link 0, the BlockAck timer
1403 * expires and the BlockAckReq is sent on another link (e.g., on link 1). When the
1404 * originator processes the BlockAck response, it will not interpret a '0' in the
1405 * bitmap corresponding to the transmitted MPDUs as a negative acknowledgment,
1406 * because the BlockAck is received on a different link than the one on which the
1407 * MPDUs are (still) inflight. Hence, such MPDUs stay inflight and are not
1408 * retransmitted.
1409 */
1410 if (isBar)
1411 {
1412 psdu->GetHeader(0).SetRetry();
1413 }
1414 else
1415 {
1416 // missed block ack after data frame with Implicit BAR Ack policy
1417 auto [reqHdr, hdr] = edca->PrepareBlockAckRequest(recipient, tid);
1418 GetBaManager(tid)->ScheduleBar(reqHdr, hdr);
1419 }
1420 resetCw = false;
1421 }
1422 else
1423 {
1424 NS_LOG_DEBUG("Missed Block Ack, do not transmit a BlockAckReq");
1425 // if a BA agreement exists, we can get here if there is no outstanding
1426 // MPDU whose lifetime has not expired yet.
1427 GetWifiRemoteStationManager()->ReportFinalDataFailed(*psdu->begin());
1428 if (isBar)
1429 {
1430 DequeuePsdu(psdu);
1431 }
1432 if (m_mac->GetBaAgreementEstablishedAsOriginator(recipient, tid))
1433 {
1434 // schedule a BlockAckRequest to be sent only if there are data frames queued
1435 // for this recipient
1436 GetBaManager(tid)->AddToSendBarIfDataQueuedList(recipientMld, tid);
1437 }
1438 resetCw = true;
1439 }
1440 }
1441 else
1442 {
1443 // we have to retransmit the data frames, if needed
1445 ->ReportAmpduTxStatus(recipient, 0, psdu->GetNMpdus(), 0, 0, txVector);
1446 if (!GetWifiRemoteStationManager()->NeedRetransmission(*psdu->begin()))
1447 {
1448 NS_LOG_DEBUG("Missed Block Ack, do not retransmit the data frames");
1449 GetWifiRemoteStationManager()->ReportFinalDataFailed(*psdu->begin());
1450 for (const auto& mpdu : *PeekPointer(psdu))
1451 {
1453 DequeueMpdu(mpdu);
1454 }
1455 resetCw = true;
1456 }
1457 else
1458 {
1459 NS_LOG_DEBUG("Missed Block Ack, retransmit data frames");
1460 GetBaManager(tid)->NotifyMissedBlockAck(m_linkId, recipientMld, tid);
1461 resetCw = false;
1462 }
1463 }
1464}
1465
1466void
1468 Time durationId,
1469 WifiTxVector& blockAckTxVector,
1470 double rxSnr)
1471{
1472 NS_LOG_FUNCTION(this << durationId << blockAckTxVector << rxSnr);
1473
1474 WifiMacHeader hdr;
1476 auto addr1 = agreement.GetPeer();
1477 if (auto originator = GetWifiRemoteStationManager()->GetAffiliatedStaAddress(addr1))
1478 {
1479 addr1 = *originator;
1480 }
1481 hdr.SetAddr1(addr1);
1482 hdr.SetAddr2(m_self);
1483 hdr.SetDsNotFrom();
1484 hdr.SetDsNotTo();
1485
1486 CtrlBAckResponseHeader blockAck;
1487 blockAck.SetType(agreement.GetBlockAckType());
1488 blockAck.SetTidInfo(agreement.GetTid());
1489 agreement.FillBlockAckBitmap(&blockAck);
1490
1491 Ptr<Packet> packet = Create<Packet>();
1492 packet->AddHeader(blockAck);
1493 Ptr<WifiPsdu> psdu = GetWifiPsdu(Create<WifiMpdu>(packet, hdr), blockAckTxVector);
1494
1495 // 802.11-2016, Section 9.2.5.7: In a BlockAck frame transmitted in response
1496 // to a BlockAckReq frame or transmitted in response to a frame containing an
1497 // implicit block ack request, the Duration/ID field is set to the value obtained
1498 // from the Duration/ ID field of the frame that elicited the response minus the
1499 // time, in microseconds between the end of the PPDU carrying the frame that
1500 // elicited the response and the end of the PPDU carrying the BlockAck frame.
1501 Time baDurationId = durationId - m_phy->GetSifs() -
1502 m_phy->CalculateTxDuration(psdu, blockAckTxVector, m_phy->GetPhyBand());
1503 // The TXOP holder may exceed the TXOP limit in some situations (Sec. 10.22.2.8 of 802.11-2016)
1504 if (baDurationId.IsStrictlyNegative())
1505 {
1506 baDurationId = Seconds(0);
1507 }
1508 psdu->GetHeader(0).SetDuration(baDurationId);
1509
1510 SnrTag tag;
1511 tag.Set(rxSnr);
1512 psdu->GetPayload(0)->AddPacketTag(tag);
1513
1514 ForwardPsduDown(psdu, blockAckTxVector);
1515}
1516
1517void
1519 RxSignalInfo rxSignalInfo,
1520 const WifiTxVector& txVector,
1521 bool inAmpdu)
1522{
1523 // The received MPDU is either broadcast or addressed to this station
1524 NS_ASSERT(mpdu->GetHeader().GetAddr1().IsGroup() || mpdu->GetHeader().GetAddr1() == m_self);
1525
1526 double rxSnr = rxSignalInfo.snr;
1527 const WifiMacHeader& hdr = mpdu->GetHeader();
1528
1529 if (hdr.IsCtl())
1530 {
1531 if (hdr.IsCts() && m_txTimer.IsRunning() &&
1533 {
1534 NS_ABORT_MSG_IF(inAmpdu, "Received CTS as part of an A-MPDU");
1535 NS_ASSERT(hdr.GetAddr1() == m_self);
1536
1537 Mac48Address sender = m_psdu->GetAddr1();
1538 NS_LOG_DEBUG("Received CTS from=" << sender);
1539
1540 SnrTag tag;
1541 mpdu->GetPacket()->PeekPacketTag(tag);
1542 GetWifiRemoteStationManager()->ReportRxOk(sender, rxSignalInfo, txVector);
1543 GetWifiRemoteStationManager()->ReportRtsOk(m_psdu->GetHeader(0),
1544 rxSnr,
1545 txVector.GetMode(),
1546 tag.Get());
1547
1548 m_txTimer.Cancel();
1552 this);
1553 }
1554 else if (hdr.IsBlockAck() && m_txTimer.IsRunning() &&
1556 {
1557 Mac48Address sender = hdr.GetAddr2();
1558 NS_LOG_DEBUG("Received BlockAck from=" << sender);
1559
1560 SnrTag tag;
1561 mpdu->GetPacket()->PeekPacketTag(tag);
1562
1563 // notify the Block Ack Manager
1564 CtrlBAckResponseHeader blockAck;
1565 mpdu->GetPacket()->PeekHeader(blockAck);
1566 uint8_t tid = blockAck.GetTidInfo();
1567 std::pair<uint16_t, uint16_t> ret =
1568 GetBaManager(tid)->NotifyGotBlockAck(m_linkId,
1569 blockAck,
1570 m_mac->GetMldAddress(sender).value_or(sender),
1571 {tid});
1572 GetWifiRemoteStationManager()->ReportAmpduTxStatus(sender,
1573 ret.first,
1574 ret.second,
1575 rxSnr,
1576 tag.Get(),
1578
1579 // cancel the timer
1580 m_txTimer.Cancel();
1582
1583 // Reset the CW
1585
1586 // if this BlockAck was sent in response to a BlockAckReq, dequeue the blockAckReq
1587 if (m_psdu && m_psdu->GetNMpdus() == 1 && m_psdu->GetHeader(0).IsBlockAckReq())
1588 {
1590 }
1591 m_psdu = nullptr;
1593 }
1594 else if (hdr.IsBlockAckReq())
1595 {
1596 NS_ASSERT(hdr.GetAddr1() == m_self);
1597 NS_ABORT_MSG_IF(inAmpdu, "BlockAckReq in A-MPDU is not supported");
1598
1599 Mac48Address sender = hdr.GetAddr2();
1600 NS_LOG_DEBUG("Received BlockAckReq from=" << sender);
1601
1602 CtrlBAckRequestHeader blockAckReq;
1603 mpdu->GetPacket()->PeekHeader(blockAckReq);
1604 NS_ABORT_MSG_IF(blockAckReq.IsMultiTid(), "Multi-TID BlockAckReq not supported");
1605 uint8_t tid = blockAckReq.GetTidInfo();
1606
1607 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(sender, tid);
1608
1609 if (!agreement)
1610 {
1611 NS_LOG_DEBUG("There's not a valid agreement for this BlockAckReq");
1612 return;
1613 }
1614
1615 GetBaManager(tid)->NotifyGotBlockAckRequest(
1616 m_mac->GetMldAddress(sender).value_or(sender),
1617 tid,
1618 blockAckReq.GetStartingSequence());
1619
1620 NS_LOG_DEBUG("Schedule Block Ack");
1622 m_phy->GetSifs(),
1624 this,
1625 *agreement,
1626 hdr.GetDuration(),
1627 GetWifiRemoteStationManager()->GetBlockAckTxVector(sender, txVector),
1628 rxSnr);
1629 }
1630 else
1631 {
1632 // the received control frame cannot be handled here
1633 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1634 }
1635 return;
1636 }
1637
1638 if (hdr.IsQosData() && hdr.HasData() && hdr.GetAddr1() == m_self)
1639 {
1640 uint8_t tid = hdr.GetQosTid();
1641
1643 {
1644 // a Block Ack agreement has been established
1645 NS_LOG_DEBUG("Received from=" << hdr.GetAddr2() << " (" << *mpdu << ")");
1646
1647 GetBaManager(tid)->NotifyGotMpdu(mpdu);
1648
1649 if (!inAmpdu && hdr.GetQosAckPolicy() == WifiMacHeader::NORMAL_ACK)
1650 {
1651 NS_LOG_DEBUG("Schedule Normal Ack");
1654 this,
1655 hdr,
1656 txVector,
1657 rxSnr);
1658 }
1659 return;
1660 }
1661 // We let the QosFrameExchangeManager handle QoS data frame not belonging
1662 // to a Block Ack agreement
1663 }
1664
1665 QosFrameExchangeManager::ReceiveMpdu(mpdu, rxSignalInfo, txVector, inAmpdu);
1666}
1667
1668void
1670 const RxSignalInfo& rxSignalInfo,
1671 const WifiTxVector& txVector,
1672 const std::vector<bool>& perMpduStatus)
1673{
1674 std::set<uint8_t> tids = psdu->GetTids();
1675
1676 // Multi-TID A-MPDUs are not supported yet
1677 if (tids.size() == 1)
1678 {
1679 uint8_t tid = *tids.begin();
1680 WifiMacHeader::QosAckPolicy ackPolicy = psdu->GetAckPolicyForTid(tid);
1681 NS_ASSERT(psdu->GetNMpdus() > 1);
1682
1683 if (ackPolicy == WifiMacHeader::NORMAL_ACK)
1684 {
1685 // Normal Ack or Implicit Block Ack Request
1686 NS_LOG_DEBUG("Schedule Block Ack");
1687 auto agreement = m_mac->GetBaAgreementEstablishedAsRecipient(psdu->GetAddr2(), tid);
1688 NS_ASSERT(agreement);
1689
1691 m_phy->GetSifs(),
1693 this,
1694 *agreement,
1695 psdu->GetDuration(),
1696 GetWifiRemoteStationManager()->GetBlockAckTxVector(psdu->GetAddr2(), txVector),
1697 rxSignalInfo.snr);
1698 }
1699 }
1700}
1701
1702} // 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.
void NotifyAckTimeoutResetNow()
Notify that ack timer has reset.
void NotifyAckTimeoutStartNow(Time duration)
Notify that ack timer has started for the given duration.
void NotifyCtsTimeoutResetNow()
Notify that CTS timer has reset.
Headers for BlockAckRequest.
Definition: ctrl-headers.h:52
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.
Definition: ctrl-headers.h:203
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.
uint16_t m_allowedWidth
the allowed width in MHz for the current transmission
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, uint16_t allowedWidth)
Request the FrameExchangeManager to start a frame exchange sequence.
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...
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
Definition: mac48-address.h:46
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.
Definition: ptr.h:77
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.
Ptr< BlockAckManager > GetBaManager()
Get the Block Ack Manager associated with this QosTxop.
Definition: qos-txop.cc:289
AcIndex GetAccessCategory() const
Get the access category of this object.
Definition: qos-txop.cc:807
void AddBaResponseTimeout(Mac48Address recipient, uint8_t tid)
Callback when ADDBA response is not received after timeout.
Definition: qos-txop.cc:747
virtual Time GetRemainingTxop(uint8_t linkId) const
Return the remaining duration in the current TXOP on the given link.
Definition: qos-txop.cc:643
uint8_t GetQosQueueSize(uint8_t tid, Mac48Address receiver) const
Get the value for the Queue Size subfield of the QoS Control field of a QoS data frame of the given T...
Definition: qos-txop.cc:182
void ResetBa(Mac48Address recipient, uint8_t tid)
Reset BA agreement after BA negotiation failed.
Definition: qos-txop.cc:760
void CompleteMpduTx(Ptr< WifiMpdu > mpdu)
Stores an MPDU (part of an A-MPDU) in block ack agreement (i.e.
Definition: qos-txop.cc:705
std::pair< CtrlBAckRequestHeader, WifiMacHeader > PrepareBlockAckRequest(Mac48Address recipient, uint8_t tid) const
Definition: qos-txop.cc:307
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:571
void Set(double snr)
Set the SNR to the given value.
Definition: snr-tag.cc:84
double Get() const
Return the SNR value.
Definition: snr-tag.cc:90
Status code for association response.
Definition: status-code.h:32
void SetSuccess()
Set success bit to 0 (success).
Definition: status-code.cc:30
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:287
bool IsStrictlyNegative() const
Exactly equivalent to t < 0.
Definition: nstime.h:342
bool IsZero() const
Exactly equivalent to t == 0.
Definition: nstime.h:315
Time GetTxopLimit() const
Return the TXOP limit.
Definition: txop.cc:590
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:382
Ptr< WifiMacQueue > GetWifiMacQueue() const
Return the packet queue associated with this Txop.
Definition: txop.cc:275
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:373
virtual void Queue(Ptr< Packet > packet, const WifiMacHeader &hdr)
Definition: txop.cc:623
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:932
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 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 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.
std::optional< Mac48Address > GetMldAddress(const Mac48Address &remoteAddr) const
Definition: wifi-mac.cc:1774
TypeOfStation GetTypeOfStation() const
Return the type of station.
Definition: wifi-mac.cc:466
uint16_t GetMpduBufferSize() const
Definition: wifi-mac.cc:2006
RecipientAgreementOptConstRef GetBaAgreementEstablishedAsRecipient(Mac48Address originator, uint8_t tid) const
Definition: wifi-mac.cc:1835
Ptr< HtConfiguration > GetHtConfiguration() const
Definition: wifi-mac.cc:1879
uint16_t GetMaxBaBufferSize(std::optional< Mac48Address > address=std::nullopt) const
Get the maximum Block Ack buffer size (in number of MPDUs) supported by the given device,...
Definition: wifi-mac.cc:1982
virtual Ptr< WifiMacQueue > GetTxopQueue(AcIndex ac) const
Get the wifi MAC queue of the (Qos)Txop associated with the given AC, if such (Qos)Txop is installed,...
Definition: wifi-mac.cc:639
Ptr< VhtConfiguration > GetVhtConfiguration() const
Definition: wifi-mac.cc:1885
bool Is6GhzBand(uint8_t linkId) const
Indicate if a given link is on the 6 GHz band.
Definition: wifi-mac.cc:1205
OriginatorAgreementOptConstRef GetBaAgreementEstablishedAsOriginator(Mac48Address recipient, uint8_t tid) const
Definition: wifi-mac.cc:1821
Mac48Address GetAddress() const
Definition: wifi-mac.cc:496
Ptr< QosTxop > GetQosTxop(AcIndex ac) const
Accessor for a specified EDCA object.
Definition: wifi-mac.cc:593
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:1759
Time GetSlot() const
Return the slot duration for this PHY.
Definition: wifi-phy.cc:825
Time GetSifs() const
Return the Short Interframe Space (SIFS) for this PHY.
Definition: wifi-phy.cc:813
static Time CalculateTxDuration(uint32_t size, const WifiTxVector &txVector, WifiPhyBand band, uint16_t staId=SU_STA_ID)
Definition: wifi-phy.cc:1539
WifiPhyBand GetPhyBand() const
Get the configured Wi-Fi band.
Definition: wifi-phy.cc:1053
static Time CalculatePhyPreambleAndHeaderDuration(const WifiTxVector &txVector)
Definition: wifi-phy.cc:1532
std::set< uint8_t > GetTids() const
Get the set of TIDs of the QoS Data frames included in the PSDU.
Definition: wifi-psdu.cc:178
const WifiMacHeader & GetHeader(std::size_t i) const
Get the header of the i-th MPDU.
Definition: wifi-psdu.cc:279
std::vector< Ptr< WifiMpdu > >::const_iterator begin() const
Return a const iterator to the first MPDU.
Definition: wifi-psdu.cc:333
uint32_t GetSize() const
Return the size of the PSDU in bytes.
Definition: wifi-psdu.cc:273
Mac48Address GetAddr1() const
Get the Receiver Address (RA), which is common to all the MPDUs.
Definition: wifi-psdu.cc:113
void SetDuration(Time duration)
Set the Duration/ID field on all the MPDUs.
Definition: wifi-psdu.cc:168
std::size_t GetNMpdus() const
Return the number of MPDUs constituting the PSDU.
Definition: wifi-psdu.cc:327
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:192
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.
uint16_t 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:66
#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:86
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:76
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#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 ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1343
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1319
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:191
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:73
@ STA
Definition: wifi-mac.h:68
@ 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:454
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:78
@ 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:186
uint32_t GetBlockAckSize(BlockAckType type)
Return the total BlockAck size (including FCS trailer).
Definition: wifi-utils.cc:68
ns3::Time timeout
RxSignalInfo structure containing info on the received signal.
Definition: phy-entity.h:69
double snr
SNR in linear scale.
Definition: phy-entity.h:70
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