wifi: (fixes #2615) Handle TXOP fragmentation
authorSébastien Deronne <sebastien.deronne@gmail.com>
Mon, 06 Feb 2017 20:32:49 +0100
changeset 12675 214c62e9a663
parent 12674 b42d10867c96
child 12676 7da0f6fc755c
wifi: (fixes #2615) Handle TXOP fragmentation
RELEASE_NOTES
src/wifi/model/dca-txop.cc
src/wifi/model/dca-txop.h
src/wifi/model/dcf-manager.h
src/wifi/model/edca-txop-n.cc
src/wifi/model/edca-txop-n.h
src/wifi/model/mac-low.cc
src/wifi/model/mac-low.h
--- a/RELEASE_NOTES	Mon Feb 06 20:31:02 2017 +0100
+++ b/RELEASE_NOTES	Mon Feb 06 20:32:49 2017 +0100
@@ -82,6 +82,7 @@
 - Bug 2613 - MaxRxSequence () is sometimes too large
 - Bug 2607 - Minstrel HT manager results in an endless loop when a 802.11ac station is transmitting to a 802.11a access point
 - Bug 2614 - RIP header version should be set to 2
+- Bug 2615 - When the TXOP limit is nonzero, packets should be fragmented so that the TXOP limit is not exceeded
 - Bug 2627 - Ipv6RawSocket does not honor the bound interface when sending packets
 - Bug 2628 - Simulation crashes because of an out of range TID
 - Bug 2629 - Assert failure in MinstrelHtWifiManager::GetLowestIndex
--- a/src/wifi/model/dca-txop.cc	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/dca-txop.cc	Mon Feb 06 20:32:49 2017 +0100
@@ -23,7 +23,6 @@
 #include "dca-txop.h"
 #include "dcf-manager.h"
 #include "dcf-state.h"
-#include "mac-low.h"
 #include "wifi-mac-queue.h"
 #include "mac-tx-middle.h"
 #include "random-stream.h"
@@ -181,28 +180,24 @@
 uint32_t
 DcaTxop::GetMinCw (void) const
 {
-  NS_LOG_FUNCTION (this);
   return m_dcf->GetCwMin ();
 }
 
 uint32_t
 DcaTxop::GetMaxCw (void) const
 {
-  NS_LOG_FUNCTION (this);
   return m_dcf->GetCwMax ();
 }
 
 uint32_t
 DcaTxop::GetAifsn (void) const
 {
-  NS_LOG_FUNCTION (this);
   return m_dcf->GetAifsn ();
 }
 
 Time
 DcaTxop::GetTxopLimit (void) const
 {
-  NS_LOG_FUNCTION (this);
   return m_dcf->GetTxopLimit ();
 }
 
@@ -375,19 +370,18 @@
                     ", to=" << m_currentHdr.GetAddr1 () <<
                     ", seq=" << m_currentHdr.GetSequenceControl ());
     }
-  MacLowTransmissionParameters params;
-  params.DisableOverrideDurationId ();
+  m_currentParams.DisableOverrideDurationId ();
   if (m_currentHdr.GetAddr1 ().IsGroup ())
     {
-      params.DisableRts ();
-      params.DisableAck ();
-      params.DisableNextData ();
-      GetLow ()->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+      m_currentParams.DisableRts ();
+      m_currentParams.DisableAck ();
+      m_currentParams.DisableNextData ();
+      GetLow ()->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
       NS_LOG_DEBUG ("tx broadcast");
     }
   else
     {
-      params.EnableAck ();
+      m_currentParams.EnableAck ();
 
       if (NeedFragmentation ())
         {
@@ -396,19 +390,19 @@
           if (IsLastFragment ())
             {
               NS_LOG_DEBUG ("fragmenting last fragment size=" << fragment->GetSize ());
-              params.DisableNextData ();
+              m_currentParams.DisableNextData ();
             }
           else
             {
               NS_LOG_DEBUG ("fragmenting size=" << fragment->GetSize ());
-              params.EnableNextData (GetNextFragmentSize ());
+              m_currentParams.EnableNextData (GetNextFragmentSize ());
             }
-          GetLow ()->StartTransmission (fragment, &hdr, params, this);
+          GetLow ()->StartTransmission (fragment, &hdr, m_currentParams, this);
         }
       else
         {
-          params.DisableNextData ();
-          GetLow ()->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+          m_currentParams.DisableNextData ();
+          GetLow ()->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
         }
     }
 }
@@ -542,19 +536,18 @@
   NextFragment ();
   WifiMacHeader hdr;
   Ptr<Packet> fragment = GetFragmentPacket (&hdr);
-  MacLowTransmissionParameters params;
-  params.EnableAck ();
-  params.DisableRts ();
-  params.DisableOverrideDurationId ();
+  m_currentParams.EnableAck ();
+  m_currentParams.DisableRts ();
+  m_currentParams.DisableOverrideDurationId ();
   if (IsLastFragment ())
     {
-      params.DisableNextData ();
+      m_currentParams.DisableNextData ();
     }
   else
     {
-      params.EnableNextData (GetNextFragmentSize ());
+      m_currentParams.EnableNextData (GetNextFragmentSize ());
     }
-  GetLow ()->StartTransmission (fragment, &hdr, params, this);
+  GetLow ()->StartTransmission (fragment, &hdr, m_currentParams, this);
 }
 
 void
--- a/src/wifi/model/dca-txop.h	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/dca-txop.h	Mon Feb 06 20:32:49 2017 +0100
@@ -21,6 +21,7 @@
 #ifndef DCA_TXOP_H
 #define DCA_TXOP_H
 
+#include "mac-low.h"
 #include "wifi-mac-header.h"
 #include "wifi-remote-station-manager.h"
 
@@ -29,7 +30,6 @@
 class DcfState;
 class DcfManager;
 class WifiMacQueue;
-class MacLow;
 class MacTxMiddle;
 class RandomStream;
 class CtrlBAckResponseHeader;
@@ -387,6 +387,7 @@
 
   Ptr<const Packet> m_currentPacket;
   WifiMacHeader m_currentHdr;
+  MacLowTransmissionParameters m_currentParams;
   uint8_t m_fragmentNumber;
 };
 
--- a/src/wifi/model/dcf-manager.h	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/dcf-manager.h	Mon Feb 06 20:32:49 2017 +0100
@@ -31,6 +31,7 @@
 class WifiPhy;
 class PhyListener;
 class DcfState;
+class MacLow;
 
 /**
  * \brief Manage a set of ns3::DcfState
--- a/src/wifi/model/edca-txop-n.cc	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/edca-txop-n.cc	Mon Feb 06 20:32:49 2017 +0100
@@ -23,7 +23,6 @@
 #include "ns3/log.h"
 #include "ns3/pointer.h"
 #include "edca-txop-n.h"
-#include "mac-low.h"
 #include "dcf-manager.h"
 #include "dcf-state.h"
 #include "mac-tx-middle.h"
@@ -71,7 +70,8 @@
     m_typeOfStation (STA),
     m_blockAckType (COMPRESSED_BLOCK_ACK),
     m_startTxop (Seconds (0)),
-    m_isAccessRequestedForRts (false)
+    m_isAccessRequestedForRts (false),
+    m_currentIsFragmented (false)
 {
   NS_LOG_FUNCTION (this);
   m_qosBlockedDestinations = new QosBlockedDestinations ();
@@ -183,6 +183,7 @@
 {
   NS_LOG_FUNCTION (this);
   m_isAccessRequestedForRts = false;
+  m_startTxop = Simulator::Now ();
   if (m_currentPacket == 0)
     {
       if (m_queue->IsEmpty () && !m_baManager->HasPackets ())
@@ -230,14 +231,13 @@
             }
         }
     }
-  MacLowTransmissionParameters params;
-  params.DisableOverrideDurationId ();
+  m_currentParams.DisableOverrideDurationId ();
   if (m_currentHdr.GetAddr1 ().IsGroup ())
     {
-      params.DisableRts ();
-      params.DisableAck ();
-      params.DisableNextData ();
-      m_low->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+      m_currentParams.DisableRts ();
+      m_currentParams.DisableAck ();
+      m_currentParams.DisableNextData ();
+      m_low->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
       NS_LOG_DEBUG ("tx broadcast");
     }
   else if (m_currentHdr.GetType () == WIFI_MAC_CTL_BACKREQ)
@@ -248,35 +248,37 @@
     {
       if (m_currentHdr.IsQosData () && m_currentHdr.IsQosBlockAck ())
         {
-          params.DisableAck ();
+          m_currentParams.DisableAck ();
         }
       else
         {
-          params.EnableAck ();
+          m_currentParams.EnableAck ();
         }
+      //With COMPRESSED_BLOCK_ACK fragmentation must be avoided.
       if (((m_currentHdr.IsQosData () && !m_currentHdr.IsQosAmsdu ())
            || (m_currentHdr.IsData () && !m_currentHdr.IsQosData ()))
           && (m_blockAckThreshold == 0 || m_blockAckType == BASIC_BLOCK_ACK)
           && NeedFragmentation ())
         {
-          //With COMPRESSED_BLOCK_ACK fragmentation must be avoided.
-          params.DisableRts ();
+          m_currentIsFragmented = true;
+          m_currentParams.DisableRts ();
           WifiMacHeader hdr;
           Ptr<Packet> fragment = GetFragmentPacket (&hdr);
           if (IsLastFragment ())
             {
               NS_LOG_DEBUG ("fragmenting last fragment size=" << fragment->GetSize ());
-              params.DisableNextData ();
+              m_currentParams.DisableNextData ();
             }
           else
             {
               NS_LOG_DEBUG ("fragmenting size=" << fragment->GetSize ());
-              params.EnableNextData (GetNextFragmentSize ());
+              m_currentParams.EnableNextData (GetNextFragmentSize ());
             }
-          m_low->StartTransmission (fragment, &hdr, params, this);
+          m_low->StartTransmission (fragment, &hdr, m_currentParams, this);
         }
       else
         {
+          m_currentIsFragmented = false;
           WifiMacHeader peekedHdr;
           Time tstamp;
           if (m_currentHdr.IsQosData ()
@@ -321,9 +323,8 @@
                   NS_LOG_DEBUG ("tx unicast A-MSDU");
                 }
             }
-          params.DisableNextData ();
-          m_startTxop = Simulator::Now ();
-          m_low->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+          m_currentParams.DisableNextData ();
+          m_low->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
           if (!GetAmpduExist (m_currentHdr.GetAddr1 ()))
             {
               CompleteTx ();
@@ -492,8 +493,8 @@
 EdcaTxopN::GotAck (void)
 {
   NS_LOG_FUNCTION (this);
-  if (!NeedFragmentation ()
-      || IsLastFragment ()
+  if (!m_currentIsFragmented
+      || !m_currentParams.HasNextPacket ()
       || m_currentHdr.IsQosAmsdu ())
     {
       NS_LOG_DEBUG ("got ack. tx done.");
@@ -539,6 +540,18 @@
   else
     {
       NS_LOG_DEBUG ("got ack. tx not done, size=" << m_currentPacket->GetSize ());
+      if (!HasTxop ())
+        {
+          if (m_currentHdr.IsQosData () && GetTxopLimit () > NanoSeconds (0))
+            {
+              m_txopTrace (m_startTxop, Simulator::Now () - m_startTxop);
+              m_cwTrace = m_dcf->GetCw ();
+              m_backoffTrace = m_rng->GetNext (0, m_dcf->GetCw ());
+              m_dcf->StartBackoffNow (m_backoffTrace);
+              m_fragmentNumber++;
+              RestartAccessIfNeeded ();
+            }
+        }
     }
 }
 
@@ -731,7 +744,7 @@
 void
 EdcaTxopN::StartAccessIfNeeded (void)
 {
-  NS_LOG_FUNCTION (this);
+  //NS_LOG_FUNCTION (this);
   if (m_currentPacket == 0
       && (!m_queue->IsEmpty () || m_baManager->HasPackets ())
       && !m_dcf->IsAccessRequested ())
@@ -821,18 +834,17 @@
         }
       return;
     }
-  MacLowTransmissionParameters params;
-  params.DisableOverrideDurationId ();
-  params.DisableNextData ();
+  m_currentParams.DisableOverrideDurationId ();
+  m_currentParams.DisableNextData ();
   if (m_currentHdr.IsQosData () && m_currentHdr.IsQosBlockAck ())
     {
-      params.DisableAck ();
+      m_currentParams.DisableAck ();
     }
   else
     {
-      params.EnableAck ();
+      m_currentParams.EnableAck ();
     }
-  if (txopLimit >= GetLow ()->CalculateOverallTxTime (peekedPacket, &hdr, params))
+  if (txopLimit >= GetLow ()->CalculateOverallTxTime (peekedPacket, &hdr, m_currentParams))
     {
       NS_LOG_DEBUG ("start next packet");
       m_currentPacket = m_queue->DequeueByTidAndAddress (&hdr,
@@ -849,7 +861,7 @@
       m_currentHdr.SetNoRetry ();
       m_fragmentNumber = 0;
       VerifyBlockAck ();
-      GetLow ()->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+      GetLow ()->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
       if (!GetAmpduExist (m_currentHdr.GetAddr1 ()))
         {
           CompleteTx ();
@@ -880,7 +892,7 @@
   NS_LOG_FUNCTION (this);
   WifiMacHeader hdr;
   Time tstamp;
-  if (!m_currentHdr.IsQosData ())
+  if (!m_currentHdr.IsQosData () || GetTxopLimit () == NanoSeconds (0))
     {
       return false;
     }
@@ -895,8 +907,7 @@
       return false;
     }
 
-  MacLowTransmissionParameters params;
-  params.DisableOverrideDurationId ();
+  MacLowTransmissionParameters params = m_currentParams;
   if (m_currentHdr.IsQosData () && m_currentHdr.IsQosBlockAck ())
     {
       params.DisableAck ();
@@ -948,8 +959,180 @@
       //HT-delayed Block Ack agreement or when it is carried in an A-MPDU.
       return false;
     }
-  return m_stationManager->NeedFragmentation (m_currentHdr.GetAddr1 (), &m_currentHdr,
-                                              m_currentPacket);
+  bool needTxopFragmentation = false;
+  if (GetTxopLimit () > NanoSeconds (0) && m_currentHdr.IsData ())
+    {
+      needTxopFragmentation = (GetLow ()->CalculateOverallTxTime (m_currentPacket, &m_currentHdr, m_currentParams) > GetTxopLimit ());
+    }
+  return (needTxopFragmentation || m_stationManager->NeedFragmentation (m_currentHdr.GetAddr1 (), &m_currentHdr, m_currentPacket));
+}
+
+bool
+EdcaTxopN::IsTxopFragmentation () const
+{
+  if (GetTxopLimit () == NanoSeconds (0))
+    {
+      return false;
+    }
+  if (!m_stationManager->NeedFragmentation (m_currentHdr.GetAddr1 (), &m_currentHdr, m_currentPacket)
+      || (GetTxopFragmentSize () < m_stationManager->GetFragmentSize (m_currentHdr.GetAddr1 (), &m_currentHdr,m_currentPacket, 0)))
+    {
+      return true;
+    }
+  return false;
+}
+
+uint32_t
+EdcaTxopN::GetTxopFragmentSize () const
+{
+  Time txopDuration = GetTxopLimit ();
+  if (txopDuration == NanoSeconds (0))
+    {
+      return 0;
+    }
+  uint32_t maxSize = m_currentPacket->GetSize ();
+  uint32_t minSize = 0;
+  uint32_t size;
+  bool found = false;
+  while (!found)
+    {
+      size = (minSize + ((maxSize - minSize) / 2));
+      if (GetLow ()->CalculateOverallTxFragmentTime (m_currentPacket, &m_currentHdr, m_currentParams, size) > txopDuration)
+        {
+          maxSize = size;
+        }
+      else
+        {
+          minSize = size;
+        }
+      if (GetLow ()->CalculateOverallTxFragmentTime (m_currentPacket, &m_currentHdr, m_currentParams, size) <= txopDuration
+          && GetLow ()->CalculateOverallTxFragmentTime (m_currentPacket, &m_currentHdr, m_currentParams, size + 1) > txopDuration)
+        {
+          found = true;
+        }
+    }
+  NS_ASSERT (size != 0);
+  return size;
+}
+
+uint32_t
+EdcaTxopN::GetNTxopFragment () const
+{
+  uint32_t fragmentSize = GetTxopFragmentSize ();
+  uint32_t nFragments = (m_currentPacket->GetSize () / fragmentSize);
+  if ((m_currentPacket->GetSize () % fragmentSize) > 0)
+    {
+      nFragments++;
+    }
+  NS_LOG_DEBUG ("GetNTxopFragment returning " << nFragments);
+  return nFragments;
+}
+
+uint32_t
+EdcaTxopN::GetTxopFragmentOffset (uint32_t fragmentNumber) const
+{
+  if (fragmentNumber == 0)
+    {
+      return 0;
+    }
+  uint32_t offset;
+  uint32_t fragmentSize = GetTxopFragmentSize ();
+  uint32_t nFragments = (m_currentPacket->GetSize () / fragmentSize);
+ if ((m_currentPacket->GetSize () % fragmentSize) > 0)
+    {
+      nFragments++;
+    }
+  if (fragmentNumber < nFragments)
+    {
+      offset = (fragmentNumber * fragmentSize);
+    }
+  else
+    {
+      NS_ASSERT (false);
+    }
+  NS_LOG_DEBUG ("GetTxopFragmentOffset returning " << offset);
+  return offset;
+}
+
+uint32_t
+EdcaTxopN::GetNextTxopFragmentSize (uint32_t fragmentNumber) const
+{
+  NS_LOG_FUNCTION (this << fragmentNumber);
+  uint32_t fragmentSize = GetTxopFragmentSize ();
+  uint32_t nFragments = GetNTxopFragment ();
+  if (fragmentNumber >= nFragments)
+    {
+      NS_LOG_DEBUG ("GetNextTxopFragmentSize returning 0");
+      return 0;
+    }
+  if (fragmentNumber == nFragments - 1)
+    {
+      fragmentSize = (m_currentPacket->GetSize () - ((nFragments - 1) * fragmentSize));
+    }
+  NS_LOG_DEBUG ("GetNextTxopFragmentSize returning " << fragmentSize);
+  return fragmentSize;
+}
+
+uint32_t
+EdcaTxopN::GetFragmentSize (void) const
+{
+  uint32_t size;
+  if (IsTxopFragmentation ())
+    {
+      size = GetNextTxopFragmentSize (m_fragmentNumber);
+    }
+  else
+    {
+      size = m_stationManager->GetFragmentSize (m_currentHdr.GetAddr1 (), &m_currentHdr,m_currentPacket, m_fragmentNumber);
+    }
+  return size;
+}
+
+uint32_t
+EdcaTxopN::GetNextFragmentSize (void) const
+ {
+  uint32_t size;
+  if (IsTxopFragmentation ())
+    {
+      size = GetNextTxopFragmentSize (m_fragmentNumber + 1);
+    }
+  else
+    {
+      size = m_stationManager->GetFragmentSize (m_currentHdr.GetAddr1 (), &m_currentHdr,m_currentPacket, m_fragmentNumber + 1);
+    }
+  return size;
+}
+
+uint32_t
+EdcaTxopN::GetFragmentOffset (void) const
+{
+  uint32_t offset;
+  if (IsTxopFragmentation ())
+    {
+      offset = GetTxopFragmentOffset (m_fragmentNumber);
+    }
+  else
+    {
+      offset = m_stationManager->GetFragmentOffset (m_currentHdr.GetAddr1 (), &m_currentHdr,
+                                                    m_currentPacket, m_fragmentNumber);
+    }
+  return offset;
+}
+
+bool
+EdcaTxopN::IsLastFragment (void) const
+{
+  bool isLastFragment;
+  if (IsTxopFragmentation ())
+    {
+      isLastFragment = (m_fragmentNumber == GetNTxopFragment () - 1);
+    }
+  else
+    {
+      isLastFragment = m_stationManager->IsLastFragment (m_currentHdr.GetAddr1 (), &m_currentHdr,
+                                                         m_currentPacket, m_fragmentNumber);
+    }
+  return isLastFragment;
 }
 
 Ptr<Packet>
@@ -1195,19 +1378,18 @@
   m_currentPacket = bar.bar;
   m_currentHdr = hdr;
 
-  MacLowTransmissionParameters params;
-  params.DisableRts ();
-  params.DisableNextData ();
-  params.DisableOverrideDurationId ();
+  m_currentParams.DisableRts ();
+  m_currentParams.DisableNextData ();
+  m_currentParams.DisableOverrideDurationId ();
   if (bar.immediate)
     {
       if (m_blockAckType == BASIC_BLOCK_ACK)
         {
-          params.EnableBasicBlockAck ();
+          m_currentParams.EnableBasicBlockAck ();
         }
       else if (m_blockAckType == COMPRESSED_BLOCK_ACK)
         {
-          params.EnableCompressedBlockAck ();
+          m_currentParams.EnableCompressedBlockAck ();
         }
       else if (m_blockAckType == MULTI_TID_BLOCK_ACK)
         {
@@ -1217,9 +1399,9 @@
   else
     {
       //Delayed block ack
-      params.EnableAck ();
+      m_currentParams.EnableAck ();
     }
-  m_low->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+  m_low->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
 }
 
 void
@@ -1307,13 +1489,12 @@
   m_currentHdr.SetNoMoreFragments ();
   m_currentHdr.SetNoRetry ();
 
-  MacLowTransmissionParameters params;
-  params.EnableAck ();
-  params.DisableRts ();
-  params.DisableNextData ();
-  params.DisableOverrideDurationId ();
+  m_currentParams.EnableAck ();
+  m_currentParams.DisableRts ();
+  m_currentParams.DisableNextData ();
+  m_currentParams.DisableOverrideDurationId ();
 
-  m_low->StartTransmission (m_currentPacket, &m_currentHdr, params, this);
+  m_low->StartTransmission (m_currentPacket, &m_currentHdr, m_currentParams, this);
 }
 
 void
--- a/src/wifi/model/edca-txop-n.h	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/edca-txop-n.h	Mon Feb 06 20:32:49 2017 +0100
@@ -463,6 +463,64 @@
    *         false otherwise.
    */
   bool HasTxop (void) const;
+  
+  /**
+   * Calculate the size of the next fragment.
+   *
+   * \return the size of the next fragment.
+   */
+  uint32_t GetNextFragmentSize (void) const;
+  /**
+   * Calculate the size of the current fragment.
+   *
+   * \return the size of the current fragment.
+   */
+  uint32_t GetFragmentSize (void) const;
+  /**
+   * Calculate the offset for the current fragment.
+   *
+   * \return the offset for the current fragment.
+   */
+  uint32_t GetFragmentOffset (void) const;
+  /**
+   * Check if the current fragment is the last fragment.
+   *
+   * \return true if the current fragment is the last fragment,
+   *         false otherwise.
+   */
+  bool IsLastFragment (void) const;
+  
+  /**
+   * Check if the current packet is fragmented because of an exceeded TXOP duration.
+   *
+   * \return true if the current packet is fragmented because of an exceeded TXOP duration,
+   *         false otherwise
+   */
+  bool IsTxopFragmentation () const;
+  /**
+   * Calculate the size of the current TXOP fragment.
+   *
+   * \return the size of the current TXOP fragment
+   */
+  uint32_t GetTxopFragmentSize () const;
+  /**
+   * Calculate the number of TXOP fragments needed for the transmission of the current packet.
+   *
+   * \return the number of TXOP fragments needed for the transmission of the current packet
+   */
+  uint32_t GetNTxopFragment () const;
+  /**
+   * Calculate the size of the next TXOP fragment.
+   *
+   * \param fragmentNumber number of the next fragment
+   */
+  uint32_t GetNextTxopFragmentSize (uint32_t fragmentNumber) const;
+  /**
+   * Calculate the offset for the fragment.
+   *
+   * \param fragmentNumber number of the fragment
+   */
+  uint32_t GetTxopFragmentOffset (uint32_t fragmentNumber) const;
 
   void DoDispose (void);
   void DoInitialize (void);
@@ -480,6 +538,7 @@
   Bar m_currentBar;
   Time m_startTxop;
   bool m_isAccessRequestedForRts;
+  bool m_currentIsFragmented;
   TracedValue<uint32_t> m_backoffTrace;
   TracedValue<uint32_t> m_cwTrace;
   TracedCallback<Time, Time> m_txopTrace;
--- a/src/wifi/model/mac-low.cc	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/mac-low.cc	Mon Feb 06 20:32:49 2017 +0100
@@ -24,6 +24,7 @@
 #include "ns3/log.h"
 #include "ns3/socket.h"
 #include "mac-low.h"
+#include "edca-txop-n.h"
 #include "wifi-mac-trailer.h"
 #include "snr-tag.h"
 #include "ampdu-tag.h"
@@ -892,10 +893,14 @@
       NS_LOG_DEBUG ("receive ack from=" << m_currentHdr.GetAddr1 ());
       SnrTag tag;
       packet->RemovePacketTag (tag);
-      m_stationManager->ReportRxOk (m_currentHdr.GetAddr1 (), &m_currentHdr,
-                                    rxSnr, txVector.GetMode ());
-      m_stationManager->ReportDataOk (m_currentHdr.GetAddr1 (), &m_currentHdr,
-                                      rxSnr, txVector.GetMode (), tag.Get ());
+      //When fragmentation is used, only update manager when the last fragment is acknowledged
+      if (!m_txParams.HasNextPacket ())
+        {
+          m_stationManager->ReportRxOk (m_currentHdr.GetAddr1 (), &m_currentHdr,
+                                        rxSnr, txVector.GetMode ());
+          m_stationManager->ReportDataOk (m_currentHdr.GetAddr1 (), &m_currentHdr,
+                                          rxSnr, txVector.GetMode (), tag.Get ());
+        }
       bool gotAck = false;
       if (m_txParams.MustWaitNormalAck ()
           && m_normalAckTimeoutEvent.IsRunning ())
@@ -915,7 +920,7 @@
         {
           m_currentDca->GotAck ();
         }
-      if (m_txParams.HasNextPacket ())
+      if (m_txParams.HasNextPacket () && (!m_currentHdr.IsQosData () || m_currentDca->GetTxopLimit () == NanoSeconds (0) || m_currentDca->HasTxop ()))
         {
           if (m_stationManager->GetRifsPermitted ())
             {
@@ -1322,6 +1327,32 @@
 }
 
 Time
+MacLow::CalculateOverallTxFragmentTime (Ptr<const Packet> packet,
+                                        const WifiMacHeader* hdr,
+                                        const MacLowTransmissionParameters& params,
+                                        uint32_t fragmentSize) const
+{
+  Time txTime = Seconds (0);
+  if (params.MustSendRts ())
+    {
+      WifiTxVector rtsTxVector = GetRtsTxVector (packet, hdr);
+      txTime += m_phy->CalculateTxDuration (GetRtsSize (), rtsTxVector, m_phy->GetFrequency ());
+      txTime += GetCtsDuration (hdr->GetAddr1 (), rtsTxVector);
+      txTime += Time (GetSifs () * 2);
+    }
+  WifiTxVector dataTxVector = GetDataTxVector (packet, hdr);
+  Ptr<const Packet> fragment = Create<Packet> (fragmentSize);
+  uint32_t dataSize = GetSize (fragment, hdr);
+  txTime += m_phy->CalculateTxDuration (dataSize, dataTxVector, m_phy->GetFrequency ());
+  txTime += GetSifs ();
+  if (params.MustWaitAck ())
+    {
+      txTime += GetAckDuration (hdr->GetAddr1 (), dataTxVector);
+    }
+  return txTime;
+}
+
+Time
 MacLow::CalculateTransmissionTime (Ptr<const Packet> packet,
                                    const WifiMacHeader* hdr,
                                    const MacLowTransmissionParameters& params) const
--- a/src/wifi/model/mac-low.h	Mon Feb 06 20:31:02 2017 +0100
+++ b/src/wifi/model/mac-low.h	Mon Feb 06 20:32:49 2017 +0100
@@ -24,7 +24,6 @@
 #define MAC_LOW_H
 
 #include "wifi-phy.h"
-#include "edca-txop-n.h"
 #include "dcf-manager.h"
 #include "wifi-remote-station-manager.h"
 #include "block-ack-agreement.h"
@@ -39,6 +38,9 @@
 namespace ns3 {
 
 class WifiMacQueue;
+class DcaTxop;
+class EdcaTxopN;
+class DcfManager;
 
 /**
  * \brief control how a packet is transmitted.
@@ -451,6 +453,11 @@
   Time CalculateOverallTxTime (Ptr<const Packet> packet,
                                const WifiMacHeader* hdr,
                                const MacLowTransmissionParameters &params) const;
+    
+  Time CalculateOverallTxFragmentTime (Ptr<const Packet> packet,
+                                       const WifiMacHeader* hdr,
+                                       const MacLowTransmissionParameters& params,
+                                       uint32_t fragmentSize) const;
 
   /**
    * \param packet packet to send