/* -*- 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;
}
}
else if(hdr->IsBeacon()) // WITHOUT MY PATCH BEACON WILL GO UP TO REGULAR_WIFI_MAC AND CRASSSH
{
NotifyRxDrop(packet);
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