wifi: Add TIM Information Element and tests for PS

This commit is contained in:
Davide Magrin
2022-02-02 21:00:41 +01:00
committed by Stefano Avallone
parent cc51f7a2f9
commit fe4cd3b362
4 changed files with 672 additions and 0 deletions

View File

@@ -111,6 +111,7 @@ set(source_files
model/supported-rates.cc
model/table-based-error-rate-model.cc
model/threshold-preamble-detection-model.cc
model/tim.cc
model/txop.cc
model/vht/vht-capabilities.cc
model/vht/vht-configuration.cc
@@ -265,6 +266,7 @@ set(header_files
model/supported-rates.h
model/table-based-error-rate-model.h
model/threshold-preamble-detection-model.h
model/tim.h
model/txop.h
model/vht/vht-capabilities.h
model/vht/vht-configuration.h
@@ -333,6 +335,7 @@ build_lib(
test/channel-access-manager-test.cc
test/inter-bss-test-suite.cc
test/power-rate-adaptation-test.cc
test/power-save-test.cc
test/spectrum-wifi-phy-test.cc
test/tx-duration-test.cc
test/wifi-aggregation-test.cc

231
src/wifi/model/tim.cc Normal file
View File

@@ -0,0 +1,231 @@
/*
* Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
*
* 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: Davide Magrin <davide@magr.in>
*/
#include "tim.h"
#include <algorithm>
#include <cstdint>
namespace ns3
{
WifiInformationElementId
Tim::ElementId() const
{
return IE_TIM;
}
uint16_t
Tim::GetInformationFieldSize() const
{
// When the TIM is carried in a non-S1G PPDU, in the event that all bits other than bit 0 in
// the traffic indication virtual bitmap are 0, the Partial Virtual Bitmap field is encoded as
// a single octet equal to 0, the Bitmap Offset subfield is 0, and the Length field is 4.
// (Sec. 9.4.2.5.1 of 802.11-2020)
// The size of the information field is the size of the Partial Virtual Bitmap field,
// plus one octet each for the DTIM Count, DTIM Period, and Bitmap Control fields
uint16_t partialVirtualBitmapSize =
GetLastNonZeroOctetIndex() - GetPartialVirtualBitmapOffset() + 1;
return partialVirtualBitmapSize + 3;
}
void
Tim::AddAid(uint16_t aid)
{
NS_ABORT_IF(aid > 2007);
m_aidValues.insert(aid);
}
bool
Tim::HasAid(uint16_t aid) const
{
return m_aidValues.find(aid) != m_aidValues.end();
}
std::set<uint16_t>
Tim::GetAidSet(uint16_t aid) const
{
auto start = m_aidValues.upper_bound(aid);
return std::set<uint16_t>(start, m_aidValues.cend());
}
void
Tim::SerializeInformationField(Buffer::Iterator start) const
{
start.WriteU8(m_dtimCount);
start.WriteU8(m_dtimPeriod);
// the Bitmap Control field is optional if the TIM is carried in an S1G PPDU, while
// it is always present when the TIM is carried in a non-S1G PPDU
start.WriteU8(GetBitmapControl());
auto partialVirtualBitmap = GetPartialVirtualBitmap();
for (auto byte : partialVirtualBitmap)
{
start.WriteU8(byte);
}
}
uint16_t
Tim::DeserializeInformationField(Buffer::Iterator start, uint16_t length)
{
NS_ABORT_MSG_IF(length < 2, "Invalid length: " << length);
m_dtimCount = start.ReadU8();
m_dtimPeriod = start.ReadU8();
if (length == 2)
{
// no Bitmap Control field nor Partial Virtual Bitmap field
return 2;
}
// Bitmap control field: here we determine the presence of multicast traffic and the offset
auto bitmapControl = start.ReadU8();
// Least significant bit is the Traffic Indication field
m_hasMulticastPending = bitmapControl & 0x01;
// Other bits are the Bitmap Offset
uint8_t partialVirtualBitmapOffset = bitmapControl & 0xFE;
// Next, deserialize the partial virtual bitmap
uint16_t octetIndex;
// length is the length of the information fields, so we need to
// subtract 3 to get the length of the Partial Virtual Bitmap
for (octetIndex = partialVirtualBitmapOffset;
octetIndex < static_cast<uint16_t>(partialVirtualBitmapOffset + length - 3);
++octetIndex)
{
if (auto octet = start.ReadU8(); octet > 0)
{
// Look for bits set to 1
for (uint8_t position = 0; position < 8; position++)
{
if ((octet >> position) & 0x1)
{
m_aidValues.insert(GetAidFromOctetIndexAndBitPosition(octetIndex, position));
}
}
}
}
return 3 + octetIndex - partialVirtualBitmapOffset;
}
uint8_t
Tim::GetAidOctetIndex(uint16_t aid) const
{
// bit number N (0 <= N <= 2007) in the bitmap corresponds to bit number (N mod 8) in octet
// number |_N / 8_| where the low order bit of each octet is bit number 0, and the high order
// bit is bit number 7 (Sec. 9.4.2.5.1 of 802.11-2020)
return (aid >> 3) & 0xff;
}
uint8_t
Tim::GetAidBit(uint16_t aid) const
{
// bit number N (0 <= N <= 2007) in the bitmap corresponds to bit number (N mod 8) in octet
// number |_N / 8_| where the low order bit of each octet is bit number 0, and the high order
// bit is bit number 7 (Sec. 9.4.2.5.1 of 802.11-2020)
return 0x01 << (aid & 0x07);
}
uint16_t
Tim::GetAidFromOctetIndexAndBitPosition(uint16_t octet, uint8_t position) const
{
return (octet << 3) + position;
}
uint8_t
Tim::GetPartialVirtualBitmapOffset() const
{
if (m_aidValues.empty())
{
return 0;
}
// N1 is the largest even number such that bits numbered 1 to (N1 * 8) 1 in the traffic
// indication virtual bitmap are all 0 (Sec. 9.4.2.5.1 of 802.11-2020).
// Examples:
// first bit set = 53, which belongs to octet 53 / 8 = 6 -> N1 = 6 (all bits 1 - 47 are zero)
// first bit set = 61, which belongs to octet 61 / 8 = 7 -> N1 = 6 (all bits 1 - 47 are zero)
return GetAidOctetIndex(*m_aidValues.cbegin()) & 0xFE;
}
uint8_t
Tim::GetLastNonZeroOctetIndex() const
{
if (m_aidValues.empty())
{
return 0;
}
// N2 is the smallest number such that bits numbered (N2 + 1) * 8 to 2007 in the traffic
// indication virtual bitmap are all 0 (Sec. 9.4.2.5.1 of 802.11-2020).
// Examples:
// last bit set = 53, which belongs to octet 53 / 8 = 6 -> N2 = 6 (all bits 56 - 2007 are zero)
// last bit set = 61, which belongs to octet 61 / 8 = 7 -> N2 = 7 (all bits 64 - 2007 are zero)
return GetAidOctetIndex(*m_aidValues.rbegin());
}
uint8_t
Tim::GetBitmapControl() const
{
// Note that setting the bitmapControl directly as the offset can be done because the least
// significant bit of the output of GetPartialVirtualBitmapOffset will always be zero, so we
// are already putting the relevant information in the appropriate part of the byte.
auto bitmapControl = GetPartialVirtualBitmapOffset();
// Set the multicast indication bit, if this is a DTIM
if ((m_dtimCount == 0) && m_hasMulticastPending)
{
bitmapControl |= 0x01;
}
return bitmapControl;
}
std::vector<uint8_t>
Tim::GetPartialVirtualBitmap() const
{
auto offset = GetPartialVirtualBitmapOffset(); // N1
// the Partial Virtual Bitmap field consists of octets numbered N1 to N2 of the traffic
// indication virtual bitmap (Sec. 9.4.2.5.1 of 802.11-2020)
std::vector<uint8_t> partialVirtualBitmap(GetLastNonZeroOctetIndex() - offset + 1, 0);
for (auto aid : m_aidValues)
{
partialVirtualBitmap.at(GetAidOctetIndex(aid) - offset) |= GetAidBit(aid);
}
return partialVirtualBitmap;
}
void
Tim::Print(std::ostream& os) const
{
os << "DTIM Count: " << +m_dtimCount << ", "
<< "DTIM Period: " << +m_dtimPeriod << ", "
<< "Has Multicast Pending: " << m_hasMulticastPending << ", AID values:";
for (uint16_t aid = 0; aid < 2008; ++aid)
{
if (HasAid(aid))
{
os << aid << " ";
}
}
}
} // namespace ns3

161
src/wifi/model/tim.h Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
*
* 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: Davide Magrin <davide@magr.in>
*/
#ifndef TIM_H
#define TIM_H
#include "wifi-information-element.h"
#include <set>
namespace ns3
{
/**
* \brief The Traffic Indication Map Information Element
* \ingroup wifi
*
* The 802.11 Traffic Indication Map (see section 9.4.2.5 of 802.11-2020)
*
* Note: The current implementation does not support S1G operation, or
* multiple BSSID.
*/
class Tim : public WifiInformationElement
{
public:
WifiInformationElementId ElementId() const override;
void Print(std::ostream& os) const override;
/**
* Add the provided AID value to the list contained in the Virtual Bitmap
*
* \param aid the AID value to add to this TIM's Virtual Bitmap
*/
void AddAid(uint16_t aid);
/**
* Add the AID values in the provided iterator range to the list contained
* in the Virtual Bitmap
*
* \tparam Iterator Type of iterator
* \param begin Starting position of the iterator range
* \param end Ending position of the iterator range
*/
template <typename Iterator>
void AddAid(Iterator begin, Iterator end);
/**
* Check whether the bit corresponding to the provided AID is set in the
* Virtual Bitmap included in this TIM
*
* \param aid The AID value to look for
* \return True if the AID value is found in the Virtual Bitmap, false otherwise
*/
bool HasAid(uint16_t aid) const;
/**
* Return the AID values, greater than the given AID value, whose corresponding bits are set
* in the virtual bitmap.
*
* \param aid the given AID value
* \return the AID values, greater than the given AID value, whose corresponding bits are set
* in the virtual bitmap
*/
std::set<uint16_t> GetAidSet(uint16_t aid = 0) const;
/**
* Get the Partial Virtual Bitmap offset, i.e., the number (denoted as N1 by the specs) of
* the first octet included in the Partial Virtual Bitmap. Note that the Bitmap Offset
* subfield contains the number N1/2.
*
* \return the Partial Virtual Bitmap offset
*/
uint8_t GetPartialVirtualBitmapOffset() const;
/**
* \return the last non-zero octet in the virtual bitmap (denoted as N2 by the specs)
*/
uint8_t GetLastNonZeroOctetIndex() const;
uint8_t m_dtimCount{0}; //!< The DTIM Count field
uint8_t m_dtimPeriod{0}; //!< The DTIM Period field
bool m_hasMulticastPending{false}; //!< Whether there is Multicast / Broadcast data
private:
uint16_t GetInformationFieldSize() const override;
void SerializeInformationField(Buffer::Iterator start) const override;
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
/**
* Obtain the index of the octet where the provided AID value should be
* set in the Virtual Bitmap
*
* \param aid the provided AID value
* \return the index of the octet where the provided AID value should be
* set in the Virtual Bitmap
*/
uint8_t GetAidOctetIndex(uint16_t aid) const;
/**
* Obtain an octet with a set bit, corresponding to the provided AID value
*
* \param aid the provided AID value
* \return an octet with a set bit, corresponding to the provided AID value
*/
uint8_t GetAidBit(uint16_t aid) const;
/**
* Obtain the AID value represented by a certain octet index and bit
* position inside the Virtual Bitmap
*
* \param octet the octet index in the Virtual Bitmap
* \param position the bit position in the octet of the Virtual Bitmap
* \return the corresponding AID value
*/
uint16_t GetAidFromOctetIndexAndBitPosition(uint16_t octet, uint8_t position) const;
/**
* The Bitmap Control field is optional if the TIM is carried in an S1G PPDU, while
* it is always present when the TIM is carried in a non-S1G PPDU.
*
* \return the value of the Bitmap Control field
*/
uint8_t GetBitmapControl() const;
/**
* \return a vector containing the Partial Virtual Bitmap octets
*/
std::vector<uint8_t> GetPartialVirtualBitmap() const;
std::set<uint16_t> m_aidValues; //!< List of AID values included in this TIM
};
template <typename Iterator>
void
Tim::AddAid(Iterator begin, Iterator end)
{
for (auto& it = begin; it != end; it++)
{
AddAid(*it);
}
}
} // namespace ns3
#endif /* TIM_H */

View File

@@ -0,0 +1,277 @@
/*
* Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
*
* 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: Davide Magrin <davide@magr.in>
*/
#include "ns3/assert.h"
#include "ns3/header-serialization-test.h"
#include "ns3/log.h"
#include "ns3/test.h"
#include "ns3/tim.h"
#include <algorithm>
#include <iterator>
#include <list>
#include <sstream>
#include <vector>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("PowerSaveTest");
/**
* \ingroup wifi-test
* \ingroup tests
*
* \brief Test TIM Information element serialization and deserialization
*/
class TimInformationElementTest : public HeaderSerializationTestCase
{
public:
/**
* \brief Constructor
*/
TimInformationElementTest();
void DoRun() override;
/**
* Reset the passed TIM to have the provided parameters.
*
* \param tim the TIM element to set
* \param dtimCount the DTIM count value
* \param dtimPeriod the DTIM period value
* \param multicastPending whether group addressed frames are queued
* \param aidValues the AID values to set
*/
void SetTim(Tim& tim,
uint8_t dtimCount,
uint8_t dtimPeriod,
bool multicastPending,
const std::list<uint16_t>& aidValues);
/**
* Test that the Bitmap Control and the Partial Virtual Bitmap
* fields of the provided TIM match the passed bufferContents.
*
* \param tim the provided TIM
* \param bufferContents the expected content of the buffer
*/
void CheckSerializationAgainstBuffer(Tim& tim, const std::vector<uint8_t>& bufferContents);
/**
* Test that the GetAidSet() method return the expected set of AID values.
*
* \param tim the TIM element
* \param aid the AID value passed to GetAidSet()
* \param expectedSet the expected set of AID values returned by GetAidSet()
*/
void CheckAidSet(const Tim& tim, uint16_t aid, const std::set<uint16_t>& expectedSet);
};
TimInformationElementTest::TimInformationElementTest()
: HeaderSerializationTestCase("Test for the TIM Information Element implementation")
{
}
void
TimInformationElementTest::SetTim(Tim& tim,
uint8_t dtimCount,
uint8_t dtimPeriod,
bool multicastPending,
const std::list<uint16_t>& aidValues)
{
tim = Tim();
tim.m_dtimCount = dtimCount;
tim.m_dtimPeriod = dtimPeriod;
tim.m_hasMulticastPending = multicastPending;
tim.AddAid(aidValues.begin(), aidValues.end());
}
void
TimInformationElementTest::CheckSerializationAgainstBuffer(
Tim& tim,
const std::vector<uint8_t>& bufferContents)
{
// Serialize the TIM
Buffer buffer;
buffer.AddAtStart(tim.GetSerializedSize());
tim.Serialize(buffer.Begin());
// Check the two Buffer instances
Buffer::Iterator bufferIterator = buffer.Begin();
for (uint32_t j = 0; j < buffer.GetSize(); j++)
{
// We skip the first four bytes, since they contain known information
if (j > 3)
{
NS_TEST_EXPECT_MSG_EQ(bufferIterator.ReadU8(),
bufferContents.at(j - 4),
"Serialization is different than provided known serialization");
}
else
{
// Advance the serialized buffer, which also contains
// the Element ID, Length, DTIM Count, DTIM Period fields
bufferIterator.ReadU8();
}
}
}
void
TimInformationElementTest::CheckAidSet(const Tim& tim,
uint16_t aid,
const std::set<uint16_t>& expectedSet)
{
auto ret = tim.GetAidSet(aid);
{
std::vector<uint16_t> diff;
// Expected set minus returned set provides expected elements that are not returned
std::set_difference(expectedSet.cbegin(),
expectedSet.cend(),
ret.cbegin(),
ret.cend(),
std::back_inserter(diff));
std::stringstream ss;
std::copy(diff.cbegin(), diff.cend(), std::ostream_iterator<uint16_t>(ss, " "));
NS_TEST_EXPECT_MSG_EQ(diff.size(),
0,
"Expected elements not returned by GetAidSet(): " << ss.str());
}
{
std::vector<uint16_t> diff;
// Returned set minus expected set provides returned elements that are not expected
std::set_difference(ret.cbegin(),
ret.cend(),
expectedSet.cbegin(),
expectedSet.cend(),
std::back_inserter(diff));
std::stringstream ss;
std::copy(diff.cbegin(), diff.cend(), std::ostream_iterator<uint16_t>(ss, " "));
NS_TEST_EXPECT_MSG_EQ(diff.size(),
0,
"Returned elements not expected by GetAidSet(): " << ss.str());
}
}
void
TimInformationElementTest::DoRun()
{
Tim tim;
// The first three examples from 802.11-2020, Annex L
//
// 1. No group addressed MSDUs, but there is traffic for STAs with AID 2 and AID 7
SetTim(tim, 0, 3, false, {2, 7});
TestHeaderSerialization(tim);
CheckSerializationAgainstBuffer(tim, {0b00000000, 0b10000100});
CheckAidSet(tim, 0, {2, 7});
CheckAidSet(tim, 1, {2, 7});
CheckAidSet(tim, 2, {7});
CheckAidSet(tim, 7, {});
//
// 2. There are group addressed MSDUs, DTIM count = 0, the nodes
// with AID 2, 7, 22, and 24 have data buffered in the AP
SetTim(tim, 0, 3, true, {2, 7, 22, 24});
TestHeaderSerialization(tim);
CheckSerializationAgainstBuffer(tim,
{
0b00000001,
// NOTE The following byte is different from the example
// in the standard. This is because the example sets the
// AID 0 bit in the partial virtual bitmap to 1. Our code
// and the example code provided in the Annex, instead, do
// not set this bit. Relevant Note from 802.11-2020,
// Section 9.4.2.5.1: "The bit numbered 0 in the traffic
// indication virtual bitmap need not be included in the
// Partial Virtual Bitmap field even if that bit is set."
0b10000100,
0b00000000,
0b01000000,
0b00000001,
});
CheckAidSet(tim, 0, {2, 7, 22, 24});
CheckAidSet(tim, 2, {7, 22, 24});
CheckAidSet(tim, 7, {22, 24});
CheckAidSet(tim, 22, {24});
CheckAidSet(tim, 24, {});
//
// 3. There are group addressed MSDUs, DTIM count = 0, only the node
// with AID 24 has data buffered in the AP
SetTim(tim, 0, 3, true, {24});
TestHeaderSerialization(tim);
CheckSerializationAgainstBuffer(tim, {0b00000011, 0b00000000, 0b00000001});
// Other arbitrary examples just to make sure
// Serialization -> Deserialization -> Serialization works
SetTim(tim, 0, 3, false, {2000});
TestHeaderSerialization(tim);
SetTim(tim, 1, 3, true, {1, 134});
TestHeaderSerialization(tim);
SetTim(tim, 1, 3, false, {1, 2});
TestHeaderSerialization(tim);
// Edge cases
//
// What if there is group addressed data only?
//
// In this case, we should still have an empty byte in the Partial Virtual Bitmap.
// From 802.11-2020: in the event that all bits other than bit 0 in the traffic indication
// virtual bitmap are 0, the Partial Virtual Bitmap field is encoded as a single octet
// equal to 0, the Bitmap Offset subfield is 0, and the Length field is 4.
SetTim(tim, 0, 3, true, {});
TestHeaderSerialization(tim);
CheckSerializationAgainstBuffer(tim, {0b00000001, 0b00000000});
NS_TEST_EXPECT_MSG_EQ(tim.GetSerializedSize() - 2, 4, "Unexpected TIM Length");
//
// What if there is no group addressed data and no unicast data?
//
// From 802.11-2020: When the TIM is carried in a non-S1G PPDU, in the event that all bits
// other than bit 0 in the traffic indication virtual bitmap are 0, the Partial Virtual Bitmap
// field is encoded as a single octet equal to 0, the Bitmap Offset subfield is 0, and the
// Length field is 4.
SetTim(tim, 0, 3, false, {});
TestHeaderSerialization(tim);
CheckSerializationAgainstBuffer(tim, {0b00000000, 0b00000000});
NS_TEST_EXPECT_MSG_EQ(tim.GetSerializedSize() - 2, 4, "Unexpected TIM Length");
}
/**
* \ingroup wifi-test
* \ingroup tests
*
* \brief Power Save Test Suite
*/
class PowerSaveTestSuite : public TestSuite
{
public:
PowerSaveTestSuite();
};
PowerSaveTestSuite::PowerSaveTestSuite()
: TestSuite("wifi-power-save", Type::UNIT)
{
AddTestCase(new TimInformationElementTest, TestCase::Duration::QUICK);
}
static PowerSaveTestSuite g_powerSaveTestSuite; ///< the test suite