A Discrete-Event Network Simulator
API
block-ack-manager.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2009, 2010 MIRKO BANCHI
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * Author: Mirko Banchi <mk.banchi@gmail.com>
19  */
20 
21 #include "ns3/log.h"
22 #include "ns3/simulator.h"
23 #include "block-ack-manager.h"
25 #include "ctrl-headers.h"
26 #include "mgt-headers.h"
27 #include "wifi-mac-queue.h"
28 #include "mac-tx-middle.h"
29 #include "qos-utils.h"
30 #include "wifi-tx-vector.h"
31 
32 namespace ns3 {
33 
34 NS_LOG_COMPONENT_DEFINE ("BlockAckManager");
35 
37 {
38  NS_LOG_FUNCTION (this);
39 }
40 
41 Bar::Bar (Ptr<const WifiMacQueueItem> bar, uint8_t tid, bool skipIfNoDataQueued)
42  : bar (bar),
43  tid (tid),
44  skipIfNoDataQueued (skipIfNoDataQueued)
45 {
46  NS_LOG_FUNCTION (this << *bar << +tid << skipIfNoDataQueued);
47 }
48 
50 
51 TypeId
53 {
54  static TypeId tid = TypeId ("ns3::BlockAckManager")
55  .SetParent<Object> ()
56  .SetGroupName ("Wifi")
57  .AddConstructor<BlockAckManager> ()
58  .AddTraceSource ("AgreementState",
59  "The state of the ADDBA handshake",
61  "ns3::BlockAckManager::AgreementStateTracedCallback")
62  ;
63  return tid;
64 }
65 
67 {
68  NS_LOG_FUNCTION (this);
69  m_retryPackets = CreateObject<WifiMacQueue> ();
70  m_retryPackets->TraceConnectWithoutContext ("Expired", MakeCallback (&BlockAckManager::NotifyDiscardedMpdu, this));
71 }
72 
74 {
75  NS_LOG_FUNCTION (this);
76  m_queue = 0;
77  m_agreements.clear ();
78  m_retryPackets = 0;
79 }
80 
81 bool
82 BlockAckManager::ExistsAgreement (Mac48Address recipient, uint8_t tid) const
83 {
84  NS_LOG_FUNCTION (this << recipient << +tid);
85  return (m_agreements.find (std::make_pair (recipient, tid)) != m_agreements.end ());
86 }
87 
88 bool
91 {
92  AgreementsCI it;
93  it = m_agreements.find (std::make_pair (recipient, tid));
94  if (it != m_agreements.end ())
95  {
96  switch (state)
97  {
99  return it->second.first.IsEstablished ();
101  return it->second.first.IsPending ();
103  return it->second.first.IsRejected ();
105  return it->second.first.IsNoReply ();
107  return it->second.first.IsReset ();
108  default:
109  NS_FATAL_ERROR ("Invalid state for block ack agreement");
110  }
111  }
112  return false;
113 }
114 
115 void
117 {
118  NS_LOG_FUNCTION (this << reqHdr << recipient);
119  std::pair<Mac48Address, uint8_t> key (recipient, reqHdr->GetTid ());
120  OriginatorBlockAckAgreement agreement (recipient, reqHdr->GetTid ());
121  agreement.SetStartingSequence (reqHdr->GetStartingSequence ());
122  /* For now we assume that originator doesn't use this field. Use of this field
123  is mandatory only for recipient */
124  agreement.SetBufferSize (reqHdr->GetBufferSize());
125  agreement.SetTimeout (reqHdr->GetTimeout ());
126  agreement.SetAmsduSupport (reqHdr->IsAmsduSupported ());
127  agreement.SetHtSupported (m_stationManager->GetHtSupported () && m_stationManager->GetHtSupported (recipient));
128  if (reqHdr->IsImmediateBlockAck ())
129  {
130  agreement.SetImmediateBlockAck ();
131  }
132  else
133  {
134  agreement.SetDelayedBlockAck ();
135  }
136  uint8_t tid = reqHdr->GetTid ();
138  agreement.SetState (OriginatorBlockAckAgreement::PENDING);
139  PacketQueue queue;
140  std::pair<OriginatorBlockAckAgreement, PacketQueue> value (agreement, queue);
141  if (ExistsAgreement (recipient, tid))
142  {
143  // Delete agreement if it exists and in RESET state
145  m_agreements.erase (key);
146  }
147  m_agreements.insert (std::make_pair (key, value));
148  m_blockPackets (recipient, reqHdr->GetTid ());
149 }
150 
151 void
153 {
154  NS_LOG_FUNCTION (this << recipient << +tid);
155  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
156  if (it != m_agreements.end ())
157  {
158  for (WifiMacQueue::ConstIterator i = m_retryPackets->begin (); i != m_retryPackets->end (); )
159  {
160  if ((*i)->GetHeader ().GetAddr1 () == recipient && (*i)->GetHeader ().GetQosTid () == tid)
161  {
162  i = m_retryPackets->Remove (i);
163  }
164  else
165  {
166  i++;
167  }
168  }
169  m_agreements.erase (it);
170  //remove scheduled BAR
171  for (std::list<Bar>::const_iterator i = m_bars.begin (); i != m_bars.end (); )
172  {
173  if (i->bar->GetHeader ().GetAddr1 () == recipient && i->tid == tid)
174  {
175  i = m_bars.erase (i);
176  }
177  else
178  {
179  i++;
180  }
181  }
182  }
183 }
184 
185 void
187  uint16_t startingSeq)
188 {
189  NS_LOG_FUNCTION (this << respHdr << recipient << startingSeq);
190  uint8_t tid = respHdr->GetTid ();
191  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
192  if (it != m_agreements.end ())
193  {
194  OriginatorBlockAckAgreement& agreement = it->second.first;
195  agreement.SetBufferSize (respHdr->GetBufferSize () + 1);
196  agreement.SetTimeout (respHdr->GetTimeout ());
197  agreement.SetAmsduSupport (respHdr->IsAmsduSupported ());
198  agreement.SetStartingSequence (startingSeq);
199  agreement.InitTxWindow ();
200  if (respHdr->IsImmediateBlockAck ())
201  {
202  agreement.SetImmediateBlockAck ();
203  }
204  else
205  {
206  agreement.SetDelayedBlockAck ();
207  }
208  if (!it->second.first.IsEstablished ())
209  {
211  }
213  if (agreement.GetTimeout () != 0)
214  {
215  Time timeout = MicroSeconds (1024 * agreement.GetTimeout ());
218  this,
219  recipient, tid);
220  }
221  }
222  m_unblockPackets (recipient, tid);
223 }
224 
227 {
228  return m_retryPackets;
229 }
230 
231 void
233 {
234  NS_LOG_FUNCTION (this << *mpdu);
235  NS_ASSERT (mpdu->GetHeader ().IsQosData ());
236 
237  uint8_t tid = mpdu->GetHeader ().GetQosTid ();
238  Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
239 
240  AgreementsI agreementIt = m_agreements.find (std::make_pair (recipient, tid));
241  NS_ASSERT (agreementIt != m_agreements.end ());
242 
243  uint16_t mpduDist = agreementIt->second.first.GetDistance (mpdu->GetHeader ().GetSequenceNumber ());
244 
245  if (mpduDist >= SEQNO_SPACE_HALF_SIZE)
246  {
247  NS_LOG_DEBUG ("Got an old packet. Do nothing");
248  return;
249  }
250 
251  // store the packet and keep the list sorted in increasing order of sequence number
252  // with respect to the starting sequence number
253  PacketQueueI it = agreementIt->second.second.begin ();
254  while (it != agreementIt->second.second.end ())
255  {
256  if (mpdu->GetHeader ().GetSequenceControl () == (*it)->GetHeader ().GetSequenceControl ())
257  {
258  NS_LOG_DEBUG ("Packet already in the queue of the BA agreement");
259  return;
260  }
261 
262  uint16_t dist = agreementIt->second.first.GetDistance ((*it)->GetHeader ().GetSequenceNumber ());
263 
264  if (mpduDist < dist ||
265  (mpduDist == dist && mpdu->GetHeader ().GetFragmentNumber () < (*it)->GetHeader ().GetFragmentNumber ()))
266  {
267  break;
268  }
269 
270  it++;
271  }
272  agreementIt->second.second.insert (it, mpdu);
273  agreementIt->second.first.NotifyTransmittedMpdu (mpdu);
274 }
275 
277 BlockAckManager::GetBar (bool remove, uint8_t tid, Mac48Address address)
278 {
280  // remove all expired MPDUs in the retransmission queue, so that Block Ack Requests
281  // (if needed) are scheduled
282  m_retryPackets->Remove (WifiMacQueue::EMPTY, true);
283 
284  auto nextBar = m_bars.begin ();
285 
286  while (nextBar != m_bars.end ())
287  {
288  Mac48Address recipient = nextBar->bar->GetHeader ().GetAddr1 ();
289 
290  if (address != Mac48Address::GetBroadcast () && tid != 8
291  && (!nextBar->bar->GetHeader ().IsBlockAckReq ()
292  || address != recipient || tid != nextBar->tid))
293  {
294  // we can only return a BAR addressed to the given station and for the given TID
295  nextBar++;
296  continue;
297  }
298  if (nextBar->bar->GetHeader ().IsBlockAckReq ())
299  {
300  AgreementsI it = m_agreements.find (std::make_pair (recipient, nextBar->tid));
301  if (it == m_agreements.end ())
302  {
303  // BA agreement was torn down; remove this BAR and continue
304  nextBar = m_bars.erase (nextBar);
305  continue;
306  }
307  if (nextBar->skipIfNoDataQueued
308  && m_retryPackets->PeekByTidAndAddress (nextBar->tid, recipient) == m_retryPackets->end ()
309  && m_queue->PeekByTidAndAddress (nextBar->tid, recipient) == m_queue->end ())
310  {
311  // skip this BAR as there is no data queued
312  nextBar++;
313  continue;
314  }
315  // remove expired outstanding MPDUs and update the starting sequence number
316  for (auto mpduIt = it->second.second.begin (); mpduIt != it->second.second.end (); )
317  {
318  if ((*mpduIt)->GetTimeStamp () + m_queue->GetMaxDelay () <= Simulator::Now ())
319  {
320  // MPDU expired
321  it->second.first.NotifyDiscardedMpdu (*mpduIt);
322  mpduIt = it->second.second.erase (mpduIt);
323  }
324  else
325  {
326  mpduIt++;
327  }
328  }
329  // update BAR if the starting sequence number changed
330  CtrlBAckRequestHeader reqHdr;
331  nextBar->bar->GetPacket ()->PeekHeader (reqHdr);
332  if (reqHdr.GetStartingSequence () != it->second.first.GetStartingSequence ())
333  {
334  reqHdr.SetStartingSequence (it->second.first.GetStartingSequence ());
335  Ptr<Packet> packet = Create<Packet> ();
336  packet->AddHeader (reqHdr);
337  nextBar->bar = Create<const WifiMacQueueItem> (packet, nextBar->bar->GetHeader ());
338  }
339  }
340 
341  bar = nextBar->bar;
342  if (remove)
343  {
344  m_bars.erase (nextBar);
345  }
346  break;
347  }
348  return bar;
349 }
350 
351 bool
353 {
354  NS_LOG_FUNCTION (this);
355  return (!m_retryPackets->IsEmpty () || GetBar (false) != 0);
356 }
357 
358 uint32_t
360 {
361  NS_LOG_FUNCTION (this << recipient << +tid);
362  AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
363  if (it == m_agreements.end ())
364  {
365  return 0;
366  }
367  uint32_t nPackets = 0;
368  PacketQueueCI queueIt = (*it).second.second.begin ();
369  while (queueIt != (*it).second.second.end ())
370  {
371  uint16_t currentSeq = (*queueIt)->GetHeader ().GetSequenceNumber ();
372  nPackets++;
373  /* a fragmented packet must be counted as one packet */
374  while (queueIt != (*it).second.second.end () && (*queueIt)->GetHeader ().GetSequenceNumber () == currentSeq)
375  {
376  queueIt++;
377  }
378  }
379  return nPackets;
380 }
381 
382 void
384 {
385  NS_LOG_FUNCTION (this << +nPackets);
386  m_blockAckThreshold = nPackets;
387 }
388 
389 void
391 {
392  NS_LOG_FUNCTION (this << manager);
393  m_stationManager = manager;
394 }
395 
396 void
398 {
399  NS_LOG_FUNCTION (this << *mpdu);
400  NS_ASSERT (mpdu->GetHeader ().IsQosData ());
401 
402  Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
403  uint8_t tid = mpdu->GetHeader ().GetQosTid ();
405 
406  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
407  NS_ASSERT (it != m_agreements.end ());
408 
409  // remove the acknowledged frame from the queue of outstanding packets
410  PacketQueueI queueIt = it->second.second.begin ();
411  while (queueIt != it->second.second.end ())
412  {
413  if ((*queueIt)->GetHeader ().GetSequenceNumber () == mpdu->GetHeader ().GetSequenceNumber ())
414  {
415  queueIt = it->second.second.erase (queueIt);
416  }
417  else
418  {
419  queueIt++;
420  }
421  }
422 
423  it->second.first.NotifyAckedMpdu (mpdu);
424 }
425 
426 void
428 {
429  NS_LOG_FUNCTION (this << *mpdu);
430  NS_ASSERT (mpdu->GetHeader ().IsQosData ());
431 
432  Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
433  uint8_t tid = mpdu->GetHeader ().GetQosTid ();
435 
436  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
437  NS_ASSERT (it != m_agreements.end ());
438 
439  // remove the frame from the queue of outstanding packets (it will be re-inserted
440  // if retransmitted)
441  PacketQueueI queueIt = it->second.second.begin ();
442  while (queueIt != it->second.second.end ())
443  {
444  if ((*queueIt)->GetHeader ().GetSequenceNumber () == mpdu->GetHeader ().GetSequenceNumber ())
445  {
446  queueIt = it->second.second.erase (queueIt);
447  }
448  else
449  {
450  queueIt++;
451  }
452  }
453 
454  // insert in the retransmission queue
455  InsertInRetryQueue (mpdu);
456 }
457 
458 void
459 BlockAckManager::NotifyGotBlockAck (const CtrlBAckResponseHeader *blockAck, Mac48Address recipient, double rxSnr, double dataSnr, WifiTxVector dataTxVector)
460 {
461  NS_LOG_FUNCTION (this << blockAck << recipient << rxSnr << dataSnr << dataTxVector);
462  if (!blockAck->IsMultiTid ())
463  {
464  uint8_t tid = blockAck->GetTidInfo ();
466  {
467  bool foundFirstLost = false;
468  uint16_t nSuccessfulMpdus = 0;
469  uint16_t nFailedMpdus = 0;
470  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
471  PacketQueueI queueEnd = it->second.second.end ();
472 
473  if (it->second.first.m_inactivityEvent.IsRunning ())
474  {
475  /* Upon reception of a BlockAck frame, the inactivity timer at the
476  originator must be reset.
477  For more details see section 11.5.3 in IEEE802.11e standard */
478  it->second.first.m_inactivityEvent.Cancel ();
479  Time timeout = MicroSeconds (1024 * it->second.first.GetTimeout ());
480  it->second.first.m_inactivityEvent = Simulator::Schedule (timeout,
482  this,
483  recipient, tid);
484  }
485 
486  uint16_t currentStartingSeq = it->second.first.GetStartingSequence ();
487  uint16_t currentSeq = SEQNO_SPACE_SIZE; // invalid value
488 
489  if (blockAck->IsBasic ())
490  {
491  for (PacketQueueI queueIt = it->second.second.begin (); queueIt != queueEnd; )
492  {
493  currentSeq = (*queueIt)->GetHeader ().GetSequenceNumber ();
494  if (blockAck->IsFragmentReceived (currentSeq,
495  (*queueIt)->GetHeader ().GetFragmentNumber ()))
496  {
497  nSuccessfulMpdus++;
498  }
499  else if (!QosUtilsIsOldPacket (currentStartingSeq, currentSeq))
500  {
501  if (!foundFirstLost)
502  {
503  foundFirstLost = true;
504  RemoveOldPackets (recipient, tid, currentSeq);
505  }
506  nFailedMpdus++;
507  InsertInRetryQueue (*queueIt);
508  }
509  // in any case, this packet is no longer outstanding
510  queueIt = it->second.second.erase (queueIt);
511  }
512  // If all frames were acknowledged, move the transmit window past the last one
513  if (!foundFirstLost && currentSeq != SEQNO_SPACE_SIZE)
514  {
515  RemoveOldPackets (recipient, tid, (currentSeq + 1) % SEQNO_SPACE_SIZE);
516  }
517  }
518  else if (blockAck->IsCompressed () || blockAck->IsExtendedCompressed ())
519  {
520  for (PacketQueueI queueIt = it->second.second.begin (); queueIt != queueEnd; )
521  {
522  currentSeq = (*queueIt)->GetHeader ().GetSequenceNumber ();
523  if (blockAck->IsPacketReceived (currentSeq))
524  {
525  it->second.first.NotifyAckedMpdu (*queueIt);
526  nSuccessfulMpdus++;
527  if (!m_txOkCallback.IsNull ())
528  {
529  m_txOkCallback (*queueIt);
530  }
531  }
532  else if (!QosUtilsIsOldPacket (currentStartingSeq, currentSeq))
533  {
534  nFailedMpdus++;
535  if (!m_txFailedCallback.IsNull ())
536  {
537  m_txFailedCallback (*queueIt);
538  }
539  InsertInRetryQueue (*queueIt);
540  }
541  // in any case, this packet is no longer outstanding
542  queueIt = it->second.second.erase (queueIt);
543  }
544  }
545  m_stationManager->ReportAmpduTxStatus (recipient, nSuccessfulMpdus, nFailedMpdus, rxSnr, dataSnr, dataTxVector);
546  }
547  }
548  else
549  {
550  //NOT SUPPORTED FOR NOW
551  NS_FATAL_ERROR ("Multi-tid block ack is not supported.");
552  }
553 }
554 
555 void
557 {
558  NS_LOG_FUNCTION (this << recipient << +tid);
560  {
561  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
562  for (auto& item : it->second.second)
563  {
564  // Queue previously transmitted packets that do not already exist in the retry queue.
565  InsertInRetryQueue (item);
566  }
567  // remove all packets from the queue of outstanding packets (they will be
568  // re-inserted if retransmitted)
569  it->second.second.clear ();
570  }
571 }
572 
573 void
575 {
576  NS_LOG_FUNCTION (this << recipient << +tid);
578  {
579  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
580  while (!it->second.second.empty ())
581  {
582  Ptr<WifiMacQueueItem> mpdu = it->second.second.front ();
583  if (it->second.first.GetDistance (mpdu->GetHeader ().GetSequenceNumber ()) >= SEQNO_SPACE_HALF_SIZE)
584  {
585  // old packet
586  it->second.second.pop_front ();
587  }
588  else
589  {
590  NotifyDiscardedMpdu (mpdu);
591  }
592  }
593  }
594 }
595 
596 void
598 {
599  NS_LOG_FUNCTION (this << *mpdu);
600 
601  if (!mpdu->GetHeader ().IsQosData ())
602  {
603  NS_LOG_DEBUG ("Not a QoS Data frame");
604  return;
605  }
606 
607  Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
608  uint8_t tid = mpdu->GetHeader ().GetQosTid ();
610  {
611  NS_LOG_DEBUG ("No established Block Ack agreement");
612  return;
613  }
614 
615  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
616  uint16_t currStartingSeq = it->second.first.GetStartingSequence ();
617  if (QosUtilsIsOldPacket (currStartingSeq, mpdu->GetHeader ().GetSequenceNumber ()))
618  {
619  NS_LOG_DEBUG ("Discarded an old frame");
620  return;
621  }
622 
623  // remove outstanding frames and frames in the retransmit queue with a sequence
624  // number less than or equal to the discarded MPDU
625  RemoveOldPackets (recipient, tid, (mpdu->GetHeader ().GetSequenceNumber () + 1) % SEQNO_SPACE_SIZE);
626  // actually advance the transmit window
627  it->second.first.NotifyDiscardedMpdu (mpdu);
628 
629  // schedule a BlockAckRequest
630  NS_LOG_DEBUG ("Schedule a Block Ack Request for agreement (" << recipient << ", " << +tid << ")");
631  Ptr<Packet> bar = Create<Packet> ();
632  bar->AddHeader (GetBlockAckReqHeader (recipient, tid));
633 
634  WifiMacHeader hdr;
636  hdr.SetAddr1 (recipient);
637  hdr.SetAddr2 (mpdu->GetHeader ().GetAddr2 ());
638  hdr.SetAddr3 (mpdu->GetHeader ().GetAddr3 ());
639  hdr.SetDsNotTo ();
640  hdr.SetDsNotFrom ();
641  hdr.SetNoRetry ();
642  hdr.SetNoMoreFragments ();
643 
644  ScheduleBar (Create<const WifiMacQueueItem> (bar, hdr));
645 }
646 
649 {
650  NS_LOG_FUNCTION (this << recipient << +tid);
651  AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
652  NS_ASSERT (it != m_agreements.end ());
653 
654  CtrlBAckRequestHeader reqHdr;
655  reqHdr.SetType ((*it).second.first.GetBlockAckReqType ());
656  reqHdr.SetTidInfo (tid);
657  reqHdr.SetStartingSequence ((*it).second.first.GetStartingSequence ());
658  return reqHdr;
659 }
660 
661 void
663 {
664  NS_LOG_FUNCTION (this << *bar);
665  NS_ASSERT (bar->GetHeader ().IsBlockAckReq () || bar->GetHeader ().IsTrigger ());
666 
667  uint8_t tid = 0;
668  if (bar->GetHeader ().IsBlockAckReq ())
669  {
670  CtrlBAckRequestHeader reqHdr;
671  bar->GetPacket ()->PeekHeader (reqHdr);
672  tid = reqHdr.GetTidInfo ();
673  }
674 #ifdef NS3_BUILD_PROFILE_DEBUG
675  else
676  {
677  CtrlTriggerHeader triggerHdr;
678  bar->GetPacket ()->PeekHeader (triggerHdr);
679  NS_ASSERT (triggerHdr.IsMuBar ());
680  }
681 #endif
682  Bar request (bar, tid, skipIfNoDataQueued);
683 
684  // if a BAR for the given agreement is present, replace it with the new one
685  std::list<Bar>::const_iterator i = m_bars.end ();
686 
687  if (bar->GetHeader ().IsBlockAckReq ())
688  {
689  for (i = m_bars.begin (); i != m_bars.end (); i++)
690  {
691  if (i->bar->GetHeader ().IsBlockAckReq ()
692  && i->bar->GetHeader ().GetAddr1 () == bar->GetHeader ().GetAddr1 () && i->tid == tid)
693  {
694  i = m_bars.erase (i);
695  break;
696  }
697  }
698  }
699 
700  if (bar->GetHeader ().IsRetry ())
701  {
702  m_bars.push_front (request);
703  }
704  else
705  {
706  m_bars.insert (i, request);
707  }
708 }
709 
710 void
712 {
713  NS_LOG_FUNCTION (this << recipient << +tid);
714  m_blockAckInactivityTimeout (recipient, tid, true);
715 }
716 
717 void
718 BlockAckManager::NotifyAgreementEstablished (Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
719 {
720  NS_LOG_FUNCTION (this << recipient << +tid << startingSeq);
721  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
722  NS_ASSERT (it != m_agreements.end ());
723  if (!it->second.first.IsEstablished ())
724  {
726  }
727  it->second.first.SetState (OriginatorBlockAckAgreement::ESTABLISHED);
728  it->second.first.SetStartingSequence (startingSeq);
729 }
730 
731 void
733 {
734  NS_LOG_FUNCTION (this << recipient << +tid);
735  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
736  NS_ASSERT (it != m_agreements.end ());
737  if (!it->second.first.IsRejected ())
738  {
740  }
741  it->second.first.SetState (OriginatorBlockAckAgreement::REJECTED);
742 }
743 
744 void
746 {
747  NS_LOG_FUNCTION (this << recipient << +tid);
748  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
749  NS_ASSERT (it != m_agreements.end ());
750  if (!it->second.first.IsNoReply ())
751  {
753  }
754  it->second.first.SetState (OriginatorBlockAckAgreement::NO_REPLY);
755  m_unblockPackets (recipient, tid);
756 }
757 
758 void
760 {
761  NS_LOG_FUNCTION (this << recipient << +tid);
762  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
763  NS_ASSERT (it != m_agreements.end ());
764  if (!it->second.first.IsReset ())
765  {
767  }
768  it->second.first.SetState (OriginatorBlockAckAgreement::RESET);
769 }
770 
771 void
773 {
774  NS_LOG_FUNCTION (this << queue);
775  m_queue = queue;
776 }
777 
778 bool
779 BlockAckManager::SwitchToBlockAckIfNeeded (Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
780 {
781  NS_LOG_FUNCTION (this << recipient << +tid << startingSeq);
783  if (!ExistsAgreementInState (recipient, tid, OriginatorBlockAckAgreement::REJECTED) && ExistsAgreement (recipient, tid))
784  {
785  uint32_t packets = m_queue->GetNPacketsByTidAndAddress (tid, recipient) +
786  GetNBufferedPackets (recipient, tid);
787  if (packets >= m_blockAckThreshold)
788  {
789  NotifyAgreementEstablished (recipient, tid, startingSeq);
790  return true;
791  }
792  }
793  return false;
794 }
795 
797 {
799  {
800  AgreementsI it = m_agreements.find (std::make_pair (recipient, tid));
801  NS_ASSERT (it != m_agreements.end ());
802 
803  // A BAR needs to be retransmitted if there is at least a non-expired outstanding MPDU
804  for (auto& mpdu : it->second.second)
805  {
806  if (mpdu->GetTimeStamp () + m_queue->GetMaxDelay () > Simulator::Now ())
807  {
808  return true;
809  }
810  }
811  }
812 
813  // If the inactivity timer has expired, QosTxop::SendDelbaFrame has been called and
814  // has destroyed the agreement, hence we get here and correctly return false
815  return false;
816 }
817 
818 void
820 {
821  RemoveFromRetryQueue (address, tid, seq, seq);
822 }
823 
824 void
825 BlockAckManager::RemoveFromRetryQueue (Mac48Address address, uint8_t tid, uint16_t startSeq, uint16_t endSeq)
826 {
827  NS_LOG_FUNCTION (this << address << +tid << startSeq << endSeq);
828 
829  AgreementsI agreementIt = m_agreements.find (std::make_pair (address, tid));
830  NS_ASSERT (agreementIt != m_agreements.end ());
831 
832  /* remove retry packet iterators if they are present in retry queue */
833  WifiMacQueue::ConstIterator it = m_retryPackets->PeekByTidAndAddress (tid, address);
834 
835  while (it != m_retryPackets->end ())
836  {
837  uint16_t itSeq = (*it)->GetHeader ().GetSequenceNumber ();
838 
839  if (agreementIt->second.first.GetDistance (itSeq) >= agreementIt->second.first.GetDistance (startSeq)
840  && agreementIt->second.first.GetDistance (itSeq) <= agreementIt->second.first.GetDistance (endSeq))
841  {
842  NS_LOG_DEBUG ("Removing frame with seqnum = " << itSeq);
843  it = m_retryPackets->Remove (it);
844  it = m_retryPackets->PeekByTidAndAddress (tid, address, it);
845  }
846  else
847  {
848  it = m_retryPackets->PeekByTidAndAddress (tid, address, ++it);
849  }
850  }
851 }
852 
853 void
854 BlockAckManager::RemoveOldPackets (Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
855 {
856  NS_LOG_FUNCTION (this << recipient << +tid << startingSeq);
857 
858  AgreementsI agreementIt = m_agreements.find (std::make_pair (recipient, tid));
859  NS_ASSERT (agreementIt != m_agreements.end ());
860  uint16_t currStartingSeq = agreementIt->second.first.GetStartingSequence ();
861 
862  NS_ABORT_MSG_IF (agreementIt->second.first.GetDistance (startingSeq) >= SEQNO_SPACE_HALF_SIZE,
863  "The new starting sequence number is an old sequence number");
864 
865  if (startingSeq == currStartingSeq)
866  {
867  return;
868  }
869 
870  // remove packets that will become old from the retransmission queue
871  uint16_t lastRemovedSeq = (startingSeq - 1 + SEQNO_SPACE_SIZE) % SEQNO_SPACE_SIZE;
872  RemoveFromRetryQueue (recipient, tid, currStartingSeq, lastRemovedSeq);
873 
874  // remove packets that will become old from the queue of outstanding packets
875  PacketQueueI it = agreementIt->second.second.begin ();
876  while (it != agreementIt->second.second.end ())
877  {
878  uint16_t itSeq = (*it)->GetHeader ().GetSequenceNumber ();
879 
880  if (agreementIt->second.first.GetDistance (itSeq) <= agreementIt->second.first.GetDistance (lastRemovedSeq))
881  {
882  NS_LOG_DEBUG ("Removing frame with seqnum = " << itSeq);
883  it = agreementIt->second.second.erase (it);
884  }
885  else
886  {
887  it++;
888  }
889  }
890 }
891 
892 void
894 {
895  NS_LOG_FUNCTION (this << &callback);
896  m_blockAckInactivityTimeout = callback;
897 }
898 
899 void
901 {
902  NS_LOG_FUNCTION (this << &callback);
903  m_blockPackets = callback;
904 }
905 
906 void
908 {
909  NS_LOG_FUNCTION (this << &callback);
910  m_unblockPackets = callback;
911 }
912 
913 void
915 {
916  NS_LOG_FUNCTION (this << txMiddle);
917  m_txMiddle = txMiddle;
918 }
919 
920 void
922 {
923  m_txOkCallback = callback;
924 }
925 
926 void
928 {
929  m_txFailedCallback = callback;
930 }
931 
932 void
934 {
935  NS_LOG_INFO ("Adding to retry queue " << *mpdu);
936  NS_ASSERT (mpdu->GetHeader ().IsQosData ());
937 
938  uint8_t tid = mpdu->GetHeader ().GetQosTid ();
939  Mac48Address recipient = mpdu->GetHeader ().GetAddr1 ();
940 
941  AgreementsI agreementIt = m_agreements.find (std::make_pair (recipient, tid));
942  NS_ASSERT (agreementIt != m_agreements.end ());
943 
944  uint16_t mpduDist = agreementIt->second.first.GetDistance (mpdu->GetHeader ().GetSequenceNumber ());
945 
946  if (mpduDist >= SEQNO_SPACE_HALF_SIZE)
947  {
948  NS_LOG_DEBUG ("Got an old packet. Do nothing");
949  return;
950  }
951 
952  WifiMacQueue::ConstIterator it = m_retryPackets->PeekByTidAndAddress (tid, recipient);
953 
954  while (it != m_retryPackets->end ())
955  {
956  if (mpdu->GetHeader ().GetSequenceControl () == (*it)->GetHeader ().GetSequenceControl ())
957  {
958  NS_LOG_DEBUG ("Packet already in the retransmit queue");
959  return;
960  }
961 
962  uint16_t dist = agreementIt->second.first.GetDistance ((*it)->GetHeader ().GetSequenceNumber ());
963 
964  if (mpduDist < dist ||
965  (mpduDist == dist && mpdu->GetHeader ().GetFragmentNumber () < (*it)->GetHeader ().GetFragmentNumber ()))
966  {
967  break;
968  }
969 
970  it = m_retryPackets->PeekByTidAndAddress (tid, recipient, ++it);
971  }
972  mpdu->GetHeader ().SetRetry ();
973  m_retryPackets->Insert (it, mpdu);
974 }
975 
976 uint16_t
978 {
979  uint16_t size = 0;
980  AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
981  if (it != m_agreements.end ())
982  {
983  size = it->second.first.GetBufferSize ();
984  }
985  return size;
986 }
987 
989 BlockAckManager::GetBlockAckReqType (Mac48Address recipient, uint8_t tid) const
990 {
991  AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
992  NS_ABORT_MSG_IF (it == m_agreements.end (), "No established Block Ack agreement");
993  return it->second.first.GetBlockAckReqType ();
994 }
995 
997 BlockAckManager::GetBlockAckType (Mac48Address recipient, uint8_t tid) const
998 {
999  AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
1000  NS_ABORT_MSG_IF (it == m_agreements.end (), "No established Block Ack agreement");
1001  return it->second.first.GetBlockAckType ();
1002 }
1003 
1004 uint16_t
1006 {
1007  uint16_t seqNum = 0;
1008  AgreementsCI it = m_agreements.find (std::make_pair (recipient, tid));
1009  if (it != m_agreements.end ())
1010  {
1011  seqNum = it->second.first.GetStartingSequence ();
1012  }
1013  return seqNum;
1014 }
1015 
1016 } //namespace ns3
void SetRetry(void)
Set the Retry bit in the Frame Control field.
void SetUnblockDestinationCallback(Callback< void, Mac48Address, uint8_t > callback)
Set unblock destination callback.
static EventId Schedule(Time const &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition: simulator.h:557
uint16_t GetTimeout(void) const
Return the timeout.
BlockAckType GetBlockAckType(Mac48Address recipient, uint8_t tid) const
This function returns the type of Block Acks sent by the recipient.
uint8_t GetTidInfo(void) const
Return the Traffic ID (TID).
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
Callback template class.
Definition: callback.h:1278
Headers for Trigger frames.
Definition: ctrl-headers.h:751
void NotifyMissedBlockAck(Mac48Address recipient, uint8_t tid)
This class mimics the TXVECTOR which is to be passed to the PHY in order to define the parameters whi...
Maintains the state and information about transmitted MPDUs with Ack Policy set to Block Ack for an o...
void RemoveOldPackets(Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
Remove packets from the retransmit queue and from the queue of outstanding packets that become old af...
std::list< Ptr< WifiMacQueueItem > >::iterator PacketQueueI
typedef for an iterator for PacketQueue.
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
std::map< std::pair< Mac48Address, uint8_t >, std::pair< OriginatorBlockAckAgreement, PacketQueue > >::const_iterator AgreementsCI
typedef for a const iterator for Agreements.
void CreateAgreement(const MgtAddBaRequestHeader *reqHdr, Mac48Address recipient)
uint32_t GetNBufferedPackets(Mac48Address recipient, uint8_t tid) const
void ScheduleBar(Ptr< const WifiMacQueueItem > bar, bool skipIfNoDataQueued=false)
TracedCallback< Time, Mac48Address, uint8_t, OriginatorBlockAckAgreement::State > m_agreementState
The trace source fired when a state transition occurred.
bool IsAmsduSupported(void) const
Return whether A-MSDU capability is supported.
uint8_t m_blockAckThreshold
block ack threshold
Agreements m_agreements
This data structure contains, for each block ack agreement (recipient, TID), a set of packets for whi...
void NotifyMissedAck(Ptr< WifiMacQueueItem > mpdu)
Invoked upon missed reception of an Ack frame after the transmission of a QoS data frame sent under a...
Implement the header for management frames of type Add Block Ack request.
Definition: mgt-headers.h:989
void SetNoMoreFragments(void)
Un-set the More Fragment bit in the Frame Control Field.
Mac48Address GetAddr1(void) const
Return the address in the Address 1 field.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file...
Definition: assert.h:67
void ReportAmpduTxStatus(Mac48Address address, uint16_t nSuccessfulMpdus, uint16_t nFailedMpdus, double rxSnr, double dataSnr, WifiTxVector dataTxVector)
Typically called per A-MPDU, either when a Block ACK was successfully received or when a BlockAckTime...
const uint16_t SEQNO_SPACE_HALF_SIZE
Size of the half the space of sequence numbers (used to determine old packets)
Definition: wifi-utils.h:225
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
uint16_t GetBufferSize(void) const
Return the buffer size.
std::map< std::pair< Mac48Address, uint8_t >, std::pair< OriginatorBlockAckAgreement, PacketQueue > >::iterator AgreementsI
typedef for an iterator for Agreements.
BlockAckReqType GetBlockAckReqType(Mac48Address recipient, uint8_t tid) const
This function returns the type of Block Acks sent to the recipient.
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
bool QosUtilsIsOldPacket(uint16_t startingSeq, uint16_t seqNumber)
This function checks if packet with sequence number seqNumber is an "old" packet. ...
Definition: qos-utils.cc:105
uint16_t GetBufferSize(void) const
Return the buffer size.
ns3::Time timeout
The different BlockAckRequest variants.
CtrlBAckRequestHeader GetBlockAckReqHeader(Mac48Address recipient, uint8_t tid) const
BlockAckRequest frame information.
uint16_t GetTimeout(void) const
Return the timeout.
bool IsExtendedCompressed(void) const
Check if the current BA policy is Extended Compressed Block Ack.
TxOk m_txOkCallback
transmit OK callback
bool IsBasic(void) const
Check if the current BA policy is Basic Block Ack.
uint8_t GetTidInfo(void) const
Return the Traffic ID (TID).
bool ExistsAgreementInState(Mac48Address recipient, uint8_t tid, OriginatorBlockAckAgreement::State state) const
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
void RemoveFromRetryQueue(Mac48Address address, uint8_t tid, uint16_t seq)
Remove an item from retransmission queue.
void SetTxFailedCallback(TxFailed callback)
uint8_t GetTid(void) const
Return the Traffic ID (TID).
void NotifyDiscardedMpdu(Ptr< const WifiMacQueueItem > mpdu)
Ptr< WifiMacQueue > GetRetransmitQueue(void)
bool IsImmediateBlockAck(void) const
Return whether the Block Ack policy is immediate Block Ack.
const WifiMacHeader & GetHeader(void) const
Get the header stored in this item.
bool IsCompressed(void) const
Check if the current BA policy is Compressed Block Ack.
void NotifyAgreementEstablished(Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
void SetAddr1(Mac48Address address)
Fill the Address 1 field with the given address.
void SetDsNotTo(void)
Un-set the To DS bit in the Frame Control field.
void SetAddr3(Mac48Address address)
Fill the Address 3 field with the given address.
uint16_t GetTimeout(void) const
Return the timeout.
void SetQueue(const Ptr< WifiMacQueue > queue)
void SetType(BlockAckReqType type)
Set the BlockAckRequest type.
void SetWifiRemoteStationManager(const Ptr< WifiRemoteStationManager > manager)
Set up WifiRemoteStationManager associated with this BlockAckManager.
static TypeId GetTypeId(void)
Get the type ID.
bool GetHtSupported(void) const
Return whether the device has HT capability support enabled.
uint8_t GetQosTid(void) const
Return the Traffic ID of a QoS header.
bool IsPacketReceived(uint16_t seq) const
Check if the packet with the given sequence number was acknowledged in this BlockAck response...
static Mac48Address GetBroadcast(void)
Headers for BlockAck response.
Definition: ctrl-headers.h:199
void NotifyAgreementRejected(Mac48Address recipient, uint8_t tid)
bool ExistsAgreement(Mac48Address recipient, uint8_t tid) const
std::list< Ptr< WifiMacQueueItem > >::const_iterator PacketQueueCI
typedef for a const iterator for PacketQueue.
Ptr< MacTxMiddle > m_txMiddle
the MacTxMiddle
void SetBlockDestinationCallback(Callback< void, Mac48Address, uint8_t > callback)
Set block destination callback.
bool NeedBarRetransmission(uint8_t tid, Mac48Address recipient)
This function returns true if a block ack agreement is established with the given recipient for the g...
void DestroyAgreement(Mac48Address recipient, uint8_t tid)
static const ConstIterator EMPTY
Invalid iterator to signal an empty queue.
void SetState(State state)
Set the current state.
void SetTxOkCallback(TxOk callback)
bool HasPackets(void)
Returns true if there are packets that need of retransmission or at least a BAR is scheduled...
uint16_t GetRecipientBufferSize(Mac48Address recipient, uint8_t tid) const
This function returns the buffer size negotiated with the recipient.
void InsertInRetryQueue(Ptr< WifiMacQueueItem > mpdu)
void SetStartingSequence(uint16_t seq)
Set starting sequence number.
void SetNoRetry(void)
Un-set the Retry bit in the Frame Control field.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
uint16_t GetSequenceNumber(void) const
Return the sequence number of the header.
address
Definition: first.py:44
void InactivityTimeout(Mac48Address recipient, uint8_t tid)
Inactivity timeout function.
void SetAddr2(Mac48Address address)
Fill the Address 2 field with the given address.
Callback< void, Mac48Address, uint8_t, bool > m_blockAckInactivityTimeout
BlockAck inactivity timeout callback.
void NotifyGotAck(Ptr< const WifiMacQueueItem > mpdu)
Invoked upon receipt of an Ack frame after the transmission of a QoS data frame sent under an establi...
void DiscardOutstandingMpdus(Mac48Address recipient, uint8_t tid)
Callback< void, Mac48Address, uint8_t > m_blockPackets
block packets callback
void StorePacket(Ptr< WifiMacQueueItem > mpdu)
an EUI-48 address
Definition: mac48-address.h:43
std::list< Ptr< WifiMacQueueItem > > PacketQueue
typedef for a list of WifiMacQueueItem.
uint16_t GetStartingSequence(void) const
Return the starting sequence number.
Ptr< WifiMacQueue > m_retryPackets
This list contains all iterators to stored packets that need to be retransmitted. ...
static Time Now(void)
Return the current simulation virtual time.
Definition: simulator.cc:195
State
Represents the state for this agreement.
void NotifyAgreementReset(Mac48Address recipient, uint8_t tid)
void SetBlockAckInactivityCallback(Callback< void, Mac48Address, uint8_t, bool > callback)
Set block ack inactivity callback.
uint16_t GetOriginatorStartingSequence(Mac48Address recipient, uint8_t tid) const
This function returns the starting sequence number of the transmit window.
void NotifyGotBlockAck(const CtrlBAckResponseHeader *blockAck, Mac48Address recipient, double rxSnr, double dataSnr, WifiTxVector dataTxVector)
bool IsFragmentReceived(uint16_t seq, uint8_t frag) const
Check if the packet with the given sequence number and fragment number was acknowledged in this Block...
void SetDelayedBlockAck(void)
Set block ack policy to delayed Ack.
EventId m_inactivityEvent
inactivity event
uint8_t GetTid(void) const
Return the Traffic ID (TID).
void SetAmsduSupport(bool supported)
Enable or disable A-MSDU support.
bool SwitchToBlockAckIfNeeded(Mac48Address recipient, uint8_t tid, uint16_t startingSeq)
bool IsMuBar(void) const
Check if this is a MU-BAR Trigger frame.
Callback< void, Mac48Address, uint8_t > m_unblockPackets
unblock packets callback
void SetType(WifiMacType type, bool resetToDsFromDs=true)
Set Type/Subtype values with the correct values depending on the given type.
The different BlockAck variants.
std::list< Bar > m_bars
list of BARs
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
Ptr< const WifiMacQueueItem > bar
BlockAckRequest or MU-BAR Trigger Frame.
void SetTxMiddle(const Ptr< MacTxMiddle > txMiddle)
Set the MacTxMiddle.
bool IsAmsduSupported(void) const
Return whether A-MSDU capability is supported.
uint8_t tid
TID (unused if MU-BAR)
void SetBlockAckThreshold(uint8_t nPackets)
Implement the header for management frames of type Add Block Ack response.
Definition: mgt-headers.h:1121
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
void UpdateAgreement(const MgtAddBaResponseHeader *respHdr, Mac48Address recipient, uint16_t startingSeq)
bool IsMultiTid(void) const
Check if the current BA policy is Multi-TID Block Ack.
void NotifyAgreementNoReply(Mac48Address recipient, uint8_t tid)
const uint16_t SEQNO_SPACE_SIZE
Size of the space of sequence numbers.
Definition: wifi-utils.h:222
void SetImmediateBlockAck(void)
Set block ack policy to immediate Ack.
uint16_t GetStartingSequence(void) const
Return the starting sequence number.
bool IsQosData(void) const
Return true if the Type is DATA and Subtype is one of the possible values for QoS Data...
void SetTimeout(uint16_t timeout)
Set timeout.
Time MicroSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1305
A base class which provides memory management and object aggregation.
Definition: object.h:87
Manages all block ack agreements for an originator station.
Headers for BlockAckRequest.
Definition: ctrl-headers.h:47
bool IsNull(void) const
Check for null implementation.
Definition: callback.h:1386
TxFailed m_txFailedCallback
transmit failed callback
uint8_t GetFragmentNumber(void) const
Return the fragment number of the header.
Ptr< const WifiMacQueueItem > GetBar(bool remove=true, uint8_t tid=8, Mac48Address recipient=Mac48Address::GetBroadcast())
Returns the next BlockAckRequest or MU-BAR Trigger Frame to send, if any.
Ptr< WifiRemoteStationManager > m_stationManager
the station manager
void SetBufferSize(uint16_t bufferSize)
Set buffer size.
a unique identifier for an interface.
Definition: type-id.h:58
void SetStartingSequence(uint16_t seq)
Set the starting sequence number from the given raw sequence control field.
bool IsImmediateBlockAck(void) const
Return whether the Block Ack policy is immediate Block Ack.
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:923
bool skipIfNoDataQueued
do not send if there is no data queued (unused if MU-BAR)
Callback< R, Ts... > MakeCallback(R(T::*memPtr)(Ts...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:1642
void InitTxWindow(void)
Initialize the originator&#39;s transmit window by setting its size and starting sequence number equal to...
void AddHeader(const Header &header)
Add header to this packet.
Definition: packet.cc:256
Implements the IEEE 802.11 MAC header.
Ptr< WifiMacQueue > m_queue
queue
uint16_t GetSequenceControl(void) const
Return the raw Sequence Control field.
void SetDsNotFrom(void)
Un-set the From DS bit in the Frame Control field.