src/devices/wifi/ap-wifi-mac.cc
changeset 6673 ec22aa763e2d
parent 6597 7fbc895f7361
child 6674 52f8688d6d01
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/devices/wifi/ap-wifi-mac.cc	Thu Dec 02 07:51:34 2010 +0000
@@ -0,0 +1,578 @@
+/* -*-  Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2006, 2009 INRIA
+ * Copyright (c) 2009 MIRKO BANCHI
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
+ * Author: Mirko Banchi <mk.banchi@gmail.com>
+ */
+#include "ap-wifi-mac.h"
+
+#include "ns3/assert.h"
+#include "ns3/log.h"
+#include "ns3/simulator.h"
+#include "ns3/string.h"
+#include "ns3/pointer.h"
+#include "ns3/boolean.h"
+
+#include "qos-tag.h"
+#include "wifi-phy.h"
+#include "dcf-manager.h"
+#include "mac-rx-middle.h"
+#include "mac-tx-middle.h"
+#include "mgt-headers.h"
+#include "mac-low.h"
+#include "amsdu-subframe-header.h"
+#include "msdu-aggregator.h"
+
+NS_LOG_COMPONENT_DEFINE ("ApWifiMac");
+
+namespace ns3 {
+
+NS_OBJECT_ENSURE_REGISTERED (ApWifiMac);
+
+TypeId
+ApWifiMac::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::ApWifiMac")
+    .SetParent<RegularWifiMac> ()
+    .AddConstructor<ApWifiMac> ()
+    .AddAttribute ("BeaconInterval", "Delay between two beacons",
+                   TimeValue (MicroSeconds (102400)),
+                   MakeTimeAccessor (&ApWifiMac::GetBeaconInterval,
+                                     &ApWifiMac::SetBeaconInterval),
+                   MakeTimeChecker ())
+    .AddAttribute ("BeaconGeneration", "Whether or not beacons are generated.",
+                   BooleanValue (true),
+                   MakeBooleanAccessor (&ApWifiMac::SetBeaconGeneration,
+                                        &ApWifiMac::GetBeaconGeneration),
+                   MakeBooleanChecker ())
+    ;
+  return tid;
+}
+
+ApWifiMac::ApWifiMac ()
+{
+  NS_LOG_FUNCTION (this);
+  m_beaconDca = CreateObject<DcaTxop> ();
+  m_beaconDca->SetAifsn(1);
+  m_beaconDca->SetMinCw(0);
+  m_beaconDca->SetMaxCw(0);
+  m_beaconDca->SetLow (m_low);
+  m_beaconDca->SetManager (m_dcfManager);
+
+  // Let the lower layers know that we are acting as an AP.
+  SetTypeOfStation (AP);
+
+  m_enableBeaconGeneration = false;
+}
+
+ApWifiMac::~ApWifiMac ()
+{
+  NS_LOG_FUNCTION (this);
+}
+
+void
+ApWifiMac::DoDispose ()
+{
+  NS_LOG_FUNCTION (this);
+  m_beaconDca = 0;
+  m_enableBeaconGeneration = false;
+  m_beaconEvent.Cancel ();
+  RegularWifiMac::DoDispose ();
+}
+
+void
+ApWifiMac::SetAddress (Mac48Address address)
+{
+  // As an AP, our MAC address is also the BSSID. Hence we are
+  // overriding this function and setting both in our parent class.
+  RegularWifiMac::SetAddress (address);
+  RegularWifiMac::SetBssid (address);
+}
+
+void
+ApWifiMac::SetBeaconGeneration (bool enable)
+{
+  NS_LOG_FUNCTION (this << enable);
+  if (!enable)
+    {
+      m_beaconEvent.Cancel ();
+    }
+  else if (enable && !m_enableBeaconGeneration)
+    {
+      m_beaconEvent = Simulator::ScheduleNow (&ApWifiMac::SendOneBeacon, this);
+    }
+  m_enableBeaconGeneration = enable;
+}
+
+bool
+ApWifiMac::GetBeaconGeneration (void) const
+{
+  return m_enableBeaconGeneration;
+}
+
+Time
+ApWifiMac::GetBeaconInterval (void) const
+{
+  return m_beaconInterval;
+}
+
+void
+ApWifiMac::SetWifiRemoteStationManager (Ptr<WifiRemoteStationManager> stationManager)
+{
+  NS_LOG_FUNCTION (this << stationManager);
+  m_beaconDca->SetWifiRemoteStationManager (stationManager);
+  RegularWifiMac::SetWifiRemoteStationManager (stationManager);
+}
+
+void
+ApWifiMac::SetLinkUpCallback (Callback<void> linkUp)
+{
+  NS_LOG_FUNCTION (this);
+  RegularWifiMac::SetLinkUpCallback (linkUp);
+
+  // The approach taken here is that, from the point of view of an AP,
+  // the link is always up, so we immediately invoke the callback if
+  // one is set
+  linkUp ();
+}
+
+void
+ApWifiMac::SetBeaconInterval (Time interval)
+{
+  NS_LOG_FUNCTION (this << interval);
+  if ((interval.GetMicroSeconds () % 1024) != 0)
+    {
+      NS_LOG_WARN ("beacon interval should be multiple of 1024us, see IEEE Std. 802.11-2007, section 11.1.1.1");
+    }
+  m_beaconInterval = interval;
+}
+
+void
+ApWifiMac::StartBeaconing (void)
+{
+  NS_LOG_FUNCTION (this);
+  SendOneBeacon ();
+}
+
+void
+ApWifiMac::ForwardDown (Ptr<const Packet> packet, Mac48Address from,
+                        Mac48Address to)
+{
+  // If we are not a QoS AP then we definitely want to use AC_BE to
+  // transmit the packet. A TID of zero will map to AC_BE (through \c
+  // QosUtilsMapTidToAc()), so we use that as our default here.
+  uint8_t tid = 0;
+
+  // If we are a QoS AP then we attempt to get a TID for this packet
+  if (m_qosSupported)
+    {
+      tid = QosUtilsGetTidForPacket (packet);
+      // Any value greater than 7 is invalid and likely indicates that
+      // the packet had no QoS tag, so we revert to zero, which'll
+      // mean that AC_BE is used.
+      if (tid >= 7)
+        {
+          tid = 0;
+        }
+    }
+
+  ForwardDown (packet, from, to, tid);
+}
+
+void
+ApWifiMac::ForwardDown (Ptr<const Packet> packet, Mac48Address from,
+                        Mac48Address to, uint8_t tid)
+{
+  NS_LOG_FUNCTION (this << packet << from << to);
+  WifiMacHeader hdr;
+
+  // For now, an AP that supports QoS does not support non-QoS
+  // associations, and vice versa. In future the AP model should
+  // support simultaneously associated QoS and non-QoS STAs, at which
+  // point there will need to be per-association QoS state maintained
+  // by the association state machine, and consulted here.
+  if (m_qosSupported)
+    {
+      hdr.SetType (WIFI_MAC_QOSDATA);
+      hdr.SetQosAckPolicy (WifiMacHeader::NORMAL_ACK);
+      hdr.SetQosNoEosp ();
+      hdr.SetQosNoAmsdu ();
+      // Transmission of multiple frames in the same TXOP is not
+      // supported for now
+      hdr.SetQosTxopLimit (0);
+      // Fill in the QoS control field in the MAC header
+      hdr.SetQosTid (tid);
+    }
+  else
+    {
+      hdr.SetTypeData ();
+    }
+
+  hdr.SetAddr1 (to);
+  hdr.SetAddr2 (GetAddress ());
+  hdr.SetAddr3 (from);
+  hdr.SetDsFrom ();
+  hdr.SetDsNotTo ();
+
+  if (m_qosSupported)
+    {
+      // Sanity check that the TID is valid
+      NS_ASSERT (tid < 8);
+      m_edca[QosUtilsMapTidToAc (tid)]->Queue (packet, hdr);
+    }
+  else
+    {
+      m_dca->Queue (packet, hdr);
+    }
+}
+
+void
+ApWifiMac::Enqueue (Ptr<const Packet> packet, Mac48Address to, Mac48Address from)
+{
+  NS_LOG_FUNCTION (this << packet << to << from);
+  if (to.IsBroadcast () || m_stationManager->IsAssociated (to))
+    {
+      ForwardDown (packet, from, to);
+    }
+}
+
+void
+ApWifiMac::Enqueue (Ptr<const Packet> packet, Mac48Address to)
+{
+  // We're sending this packet with a from address that is our own. We
+  // get that address from the lower MAC and make use of the
+  // from-spoofing Enqueue() method to avoid duplicated code.
+  Enqueue (packet, to, m_low->GetAddress ());
+}
+
+bool
+ApWifiMac::SupportsSendFrom (void) const
+{
+  return true;
+}
+
+SupportedRates
+ApWifiMac::GetSupportedRates (void) const
+{
+  // send the set of supported rates and make sure that we indicate
+  // the Basic Rate set in this set of supported rates.
+  SupportedRates rates;
+  for (uint32_t i = 0; i < m_phy->GetNModes (); i++)
+    {
+      WifiMode mode = m_phy->GetMode (i);
+      rates.AddSupportedRate (mode.GetDataRate ());
+    }
+  // set the basic rates
+  for (uint32_t j = 0; j < m_stationManager->GetNBasicModes (); j++)
+    {
+      WifiMode mode = m_stationManager->GetBasicMode (j);
+      rates.SetBasicRate (mode.GetDataRate ());
+    }
+  return rates;
+}
+
+void
+ApWifiMac::SendProbeResp (Mac48Address to)
+{
+  NS_LOG_FUNCTION (this << to);
+  WifiMacHeader hdr;
+  hdr.SetProbeResp ();
+  hdr.SetAddr1 (to);
+  hdr.SetAddr2 (GetAddress ());
+  hdr.SetAddr3 (GetAddress ());
+  hdr.SetDsNotFrom ();
+  hdr.SetDsNotTo ();
+  Ptr<Packet> packet = Create<Packet> ();
+  MgtProbeResponseHeader probe;
+  probe.SetSsid (GetSsid ());
+  probe.SetSupportedRates (GetSupportedRates ());
+  probe.SetBeaconIntervalUs (m_beaconInterval.GetMicroSeconds ());
+  packet->AddHeader (probe);
+
+  // The standard is not clear on the correct queue for management
+  // frames if we are a QoS AP. The approach taken here is to always
+  // use the DCF for these regardless of whether we have a QoS
+  // association or not.
+  m_dca->Queue (packet, hdr);
+}
+
+void
+ApWifiMac::SendAssocResp (Mac48Address to, bool success)
+{
+  NS_LOG_FUNCTION (this << to << success);
+  WifiMacHeader hdr;
+  hdr.SetAssocResp ();
+  hdr.SetAddr1 (to);
+  hdr.SetAddr2 (GetAddress ());
+  hdr.SetAddr3 (GetAddress ());
+  hdr.SetDsNotFrom ();
+  hdr.SetDsNotTo ();
+  Ptr<Packet> packet = Create<Packet> ();
+  MgtAssocResponseHeader assoc;
+  StatusCode code;
+  if (success)
+    {
+      code.SetSuccess ();
+    }
+  else
+    {
+      code.SetFailure ();
+    }
+  assoc.SetSupportedRates (GetSupportedRates ());
+  assoc.SetStatusCode (code);
+  packet->AddHeader (assoc);
+
+  // The standard is not clear on the correct queue for management
+  // frames if we are a QoS AP. The approach taken here is to always
+  // use the DCF for these regardless of whether we have a QoS
+  // association or not.
+  m_dca->Queue (packet, hdr);
+}
+
+void
+ApWifiMac::SendOneBeacon (void)
+{
+  NS_LOG_FUNCTION (this);
+  WifiMacHeader hdr;
+  hdr.SetBeacon ();
+  hdr.SetAddr1 (Mac48Address::GetBroadcast ());
+  hdr.SetAddr2 (GetAddress ());
+  hdr.SetAddr3 (GetAddress ());
+  hdr.SetDsNotFrom ();
+  hdr.SetDsNotTo ();
+  Ptr<Packet> packet = Create<Packet> ();
+  MgtBeaconHeader beacon;
+  beacon.SetSsid (GetSsid ());
+  beacon.SetSupportedRates (GetSupportedRates ());
+  beacon.SetBeaconIntervalUs (m_beaconInterval.GetMicroSeconds ());
+
+  packet->AddHeader (beacon);
+
+  // The beacon has it's own special queue, so we load it in there
+  m_beaconDca->Queue (packet, hdr);
+  m_beaconEvent = Simulator::Schedule (m_beaconInterval, &ApWifiMac::SendOneBeacon, this);
+}
+
+void
+ApWifiMac::TxOk (const WifiMacHeader &hdr)
+{
+  NS_LOG_FUNCTION (this);
+  RegularWifiMac::TxOk (hdr);
+
+  if (hdr.IsAssocResp () &&
+      m_stationManager->IsWaitAssocTxOk (hdr.GetAddr1 ()))
+    {
+      NS_LOG_DEBUG ("associated with sta="<<hdr.GetAddr1 ());
+      m_stationManager->RecordGotAssocTxOk (hdr.GetAddr1 ());
+    }
+}
+
+void
+ApWifiMac::TxFailed (const WifiMacHeader &hdr)
+{
+  NS_LOG_FUNCTION (this);
+  RegularWifiMac::TxFailed (hdr);
+
+  if (hdr.IsAssocResp () &&
+      m_stationManager->IsWaitAssocTxOk (hdr.GetAddr1 ()))
+    {
+      NS_LOG_DEBUG ("assoc failed with sta="<<hdr.GetAddr1 ());
+      m_stationManager->RecordGotAssocTxFailed (hdr.GetAddr1 ());
+    }
+}
+
+void
+ApWifiMac::Receive (Ptr<Packet> packet, const WifiMacHeader *hdr)
+{
+  NS_LOG_FUNCTION (this << packet << hdr);
+
+  Mac48Address from = hdr->GetAddr2 ();
+
+  if (hdr->IsData ())
+    {
+      Mac48Address bssid = hdr->GetAddr1 ();
+      if (!hdr->IsFromDs () &&
+          hdr->IsToDs () &&
+          bssid == GetAddress () &&
+          m_stationManager->IsAssociated (from))
+        {
+          Mac48Address to = hdr->GetAddr3 ();
+          if (to == GetAddress ())
+            {
+              NS_LOG_DEBUG ("frame for me from="<<from);
+              if (hdr->IsQosData ())
+                {
+                  if (hdr->IsQosAmsdu ())
+                    {
+                      NS_LOG_DEBUG ("Received A-MSDU from="<<from<<", size="<<packet->GetSize ());
+                      DeaggregateAmsduAndForward (packet, hdr);
+                      packet = 0;
+                    }
+                  else
+                    {
+                      ForwardUp (packet, from, bssid);
+                    }
+                }
+              else
+                {
+                  ForwardUp (packet, from, bssid);
+                }
+            }
+          else if (to.IsGroup () ||
+                   m_stationManager->IsAssociated (to))
+            {
+              NS_LOG_DEBUG ("forwarding frame from="<<from<<", to="<<to);
+              Ptr<Packet> copy = packet->Copy ();
+
+              // If the frame we are forwarding is of type QoS Data,
+              // then we need to preserve the UP in the QoS control
+              // header...
+              if (hdr->IsQosData ())
+                {
+                  ForwardDown (packet, from, to, hdr->GetQosTid ());
+                }
+              else
+                {
+                  ForwardDown (packet, from, to);
+                }
+              ForwardUp (copy, from, to);
+            }
+          else
+            {
+              ForwardUp (packet, from, to);
+            }
+        }
+      else if (hdr->IsFromDs () &&
+               hdr->IsToDs ())
+        {
+          // this is an AP-to-AP frame
+          // we ignore for now.
+          NotifyRxDrop (packet);
+        }
+      else
+        {
+          // we can ignore these frames since
+          // they are not targeted at the AP
+          NotifyRxDrop (packet);
+        }
+      return;
+    }
+  else if (hdr->IsMgt ())
+    {
+      if (hdr->IsProbeReq ())
+        {
+          NS_ASSERT (hdr->GetAddr1 ().IsBroadcast ());
+          SendProbeResp (from);
+          return;
+        }
+      else if (hdr->GetAddr1 () == GetAddress ())
+        {
+          if (hdr->IsAssocReq ())
+            {
+              // first, verify that the the station's supported
+              // rate set is compatible with our Basic Rate set
+              MgtAssocRequestHeader assocReq;
+              packet->RemoveHeader (assocReq);
+              SupportedRates rates = assocReq.GetSupportedRates ();
+              bool problem = false;
+              for (uint32_t i = 0; i < m_stationManager->GetNBasicModes (); i++)
+                {
+                  WifiMode mode = m_stationManager->GetBasicMode (i);
+                  if (!rates.IsSupportedRate (mode.GetDataRate ()))
+                    {
+                      problem = true;
+                      break;
+                    }
+                }
+              if (problem)
+                {
+                  // one of the Basic Rate set mode is not
+                  // supported by the station. So, we return an assoc
+                  // response with an error status.
+                  SendAssocResp (hdr->GetAddr2 (), false);
+                }
+              else
+                {
+                  // station supports all rates in Basic Rate Set.
+                  // record all its supported modes in its associated WifiRemoteStation
+                  for (uint32_t j = 0; j < m_phy->GetNModes (); j++)
+                    {
+                      WifiMode mode = m_phy->GetMode (j);
+                      if (rates.IsSupportedRate (mode.GetDataRate ()))
+                        {
+                          m_stationManager->AddSupportedMode (from, mode);
+                        }
+                    }
+                  m_stationManager->RecordWaitAssocTxOk (from);
+                  // send assoc response with success status.
+                  SendAssocResp (hdr->GetAddr2 (), true);
+                }
+              return;
+            }
+          else if (hdr->IsDisassociation ())
+            {
+              m_stationManager->RecordDisassociated (from);
+              return;
+            }
+        }
+    }
+
+  // Invoke the receive handler of our parent class to deal with any
+  // other frames. Specifically, this will handle Block Ack-related
+  // Management Action frames.
+  RegularWifiMac::Receive (packet, hdr);
+}
+
+void
+ApWifiMac::DeaggregateAmsduAndForward (Ptr<Packet> aggregatedPacket,
+                                       const WifiMacHeader *hdr)
+{
+  MsduAggregator::DeaggregatedMsdus packets =
+    MsduAggregator::Deaggregate (aggregatedPacket);
+
+  for (MsduAggregator::DeaggregatedMsdusCI i = packets.begin ();
+       i != packets.end (); ++i)
+    {
+      if ((*i).second.GetDestinationAddr () == GetAddress ())
+        {
+          ForwardUp ((*i).first, (*i).second.GetSourceAddr (),
+                     (*i).second.GetDestinationAddr ());
+        }
+      else
+        {
+          Mac48Address from = (*i).second.GetSourceAddr ();
+          Mac48Address to = (*i).second.GetDestinationAddr ();
+          NS_LOG_DEBUG ("forwarding QoS frame from="<<from<<", to="<<to);
+          ForwardDown ((*i).first, from, to, hdr->GetQosTid ());
+        }
+    }
+}
+
+void
+ApWifiMac::DoStart (void)
+{
+  m_beaconDca->Start ();
+  m_beaconEvent.Cancel ();
+  if (m_enableBeaconGeneration)
+    {
+      m_beaconEvent = Simulator::ScheduleNow (&ApWifiMac::SendOneBeacon, this);
+    }
+  RegularWifiMac::DoStart ();
+}
+
+} // namespace ns3