--- /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