wifi: Add support for IE fragmentation
This commit is contained in:
@@ -23,6 +23,7 @@ Release 3-dev
|
||||
- (wifi) CCA has been reworked to report the channel type in the CCA-BUSY indication and the per-20 MHz CCA bitmap for 802.11ax.
|
||||
- (wifi) PPDUs are transmitted on the largest primary channel that is found to be idle (according to the CCA-BUSY indication provided by the PHY) when gaining a TXOP.
|
||||
- (internet) Add auto-generate ARP/NDISC cache, as the outcome of GSoC 2022 project.
|
||||
- (wifi) Add support for fragmentation of Information Elements.
|
||||
|
||||
### Bugs fixed
|
||||
|
||||
|
||||
@@ -34,7 +34,21 @@ WifiInformationElement::Print (std::ostream &os) const
|
||||
uint16_t
|
||||
WifiInformationElement::GetSerializedSize () const
|
||||
{
|
||||
return (2 + GetInformationFieldSize ());
|
||||
uint16_t size = GetInformationFieldSize ();
|
||||
|
||||
if (size <= 255) // size includes the Element ID Extension field
|
||||
{
|
||||
return (2 + size);
|
||||
}
|
||||
|
||||
// the element needs to be fragmented (Sec. 10.28.11 of 802.11-2020)
|
||||
// Let M be the number of IEs of maximum size
|
||||
uint16_t m = size / 255;
|
||||
// N equals 1 if an IE not of maximum size is present at the end, 0 otherwise
|
||||
uint8_t remainder = size % 255;
|
||||
uint8_t n = (remainder > 0) ? 1 : 0;
|
||||
|
||||
return m * (2 + 255) + n * (2 + remainder);
|
||||
}
|
||||
|
||||
WifiInformationElementId
|
||||
@@ -46,22 +60,74 @@ WifiInformationElement::ElementIdExt () const
|
||||
Buffer::Iterator
|
||||
WifiInformationElement::Serialize (Buffer::Iterator i) const
|
||||
{
|
||||
auto size = GetInformationFieldSize ();
|
||||
|
||||
if (size > 255)
|
||||
{
|
||||
return SerializeFragments (i, size);
|
||||
}
|
||||
|
||||
i.WriteU8 (ElementId ());
|
||||
i.WriteU8 (GetInformationFieldSize ());
|
||||
i.WriteU8 (size);
|
||||
if (ElementId () == IE_EXTENSION)
|
||||
{
|
||||
i.WriteU8 (ElementIdExt ());
|
||||
SerializeInformationField (i);
|
||||
i.Next (GetInformationFieldSize () - 1);
|
||||
i.Next (size - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializeInformationField (i);
|
||||
i.Next (GetInformationFieldSize ());
|
||||
i.Next (size);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
Buffer::Iterator
|
||||
WifiInformationElement::SerializeFragments (Buffer::Iterator i, uint16_t size) const
|
||||
{
|
||||
NS_ASSERT (size > 255);
|
||||
// let the subclass serialize the IE in a temporary buffer
|
||||
Buffer buffer;
|
||||
buffer.AddAtStart (size);
|
||||
Buffer::Iterator source = buffer.Begin ();
|
||||
SerializeInformationField (source);
|
||||
|
||||
// Let M be the number of IEs of maximum size
|
||||
uint16_t m = size / 255;
|
||||
|
||||
for (uint16_t index = 0; index < m; index++)
|
||||
{
|
||||
i.WriteU8 ((index == 0) ? ElementId () : IE_FRAGMENT);
|
||||
i.WriteU8 (255);
|
||||
uint8_t length = 255;
|
||||
if (index == 0 && ElementId () == IE_EXTENSION)
|
||||
{
|
||||
i.WriteU8 (ElementIdExt ());
|
||||
length = 254;
|
||||
}
|
||||
for (uint8_t count = 0; count < length; count++)
|
||||
{
|
||||
i.WriteU8 (source.ReadU8 ());
|
||||
}
|
||||
}
|
||||
|
||||
// last fragment
|
||||
uint8_t remainder = size % 255;
|
||||
|
||||
if (remainder > 0)
|
||||
{
|
||||
i.WriteU8 (IE_FRAGMENT);
|
||||
i.WriteU8 (remainder);
|
||||
for (uint8_t count = 0; count < remainder; count++)
|
||||
{
|
||||
i.WriteU8 (source.ReadU8 ());
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
Buffer::Iterator
|
||||
WifiInformationElement::Deserialize (Buffer::Iterator i)
|
||||
{
|
||||
@@ -93,23 +159,74 @@ WifiInformationElement::DeserializeIfPresent (Buffer::Iterator i)
|
||||
|
||||
uint16_t length = i.ReadU8 ();
|
||||
if (ElementId () == IE_EXTENSION)
|
||||
{
|
||||
uint8_t elementIdExt = i.ReadU8 ();
|
||||
//If the element here isn't the one we're after then we immediately
|
||||
//return the iterator we were passed indicating that we haven't
|
||||
//taken anything from the buffer.
|
||||
if (elementIdExt != ElementIdExt ())
|
||||
{
|
||||
return start;
|
||||
}
|
||||
DeserializeInformationField (i, length - 1);
|
||||
i.Next (length - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
DeserializeInformationField (i, length);
|
||||
i.Next (length);
|
||||
}
|
||||
{
|
||||
uint8_t elementIdExt = i.ReadU8 ();
|
||||
//If the element here isn't the one we're after then we immediately
|
||||
//return the iterator we were passed indicating that we haven't
|
||||
//taken anything from the buffer.
|
||||
if (elementIdExt != ElementIdExt ())
|
||||
{
|
||||
return start;
|
||||
}
|
||||
length--;
|
||||
}
|
||||
|
||||
return DoDeserialize (i, length);
|
||||
}
|
||||
|
||||
Buffer::Iterator
|
||||
WifiInformationElement::DoDeserialize (Buffer::Iterator i, uint16_t length)
|
||||
{
|
||||
uint16_t limit = (ElementId () == IE_EXTENSION) ? 254 : 255;
|
||||
|
||||
auto tmp = i;
|
||||
tmp.Next (length); // tmp points to past the last byte of the IE/first fragment
|
||||
|
||||
if (length < limit || tmp.IsEnd () || (tmp.PeekU8 () != IE_FRAGMENT))
|
||||
{
|
||||
// no fragments
|
||||
DeserializeInformationField (i, length);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
NS_ASSERT (length == limit);
|
||||
|
||||
// the IE is fragmented, create a new buffer for the subclass to deserialize from.
|
||||
// Such a destination buffer will not contain the Element ID and Length fields
|
||||
Buffer buffer; // destination buffer
|
||||
buffer.AddAtStart (length); // size of the first fragment
|
||||
Buffer::Iterator bufferIt = buffer.Begin ();
|
||||
|
||||
uint16_t count = length;
|
||||
length = 0; // reset length
|
||||
|
||||
// Loop invariant:
|
||||
// - i points to the first byte of the fragment to copy (current fragment)
|
||||
// - bufferIt points to the first location of the destionation buffer to write
|
||||
// - there is room in the destination buffer to write the current fragment
|
||||
// - count is the size in bytes of the current fragment
|
||||
// - length is the number of bytes written into the destination buffer
|
||||
while (true)
|
||||
{
|
||||
for (uint8_t index = 0; index < count; index++)
|
||||
{
|
||||
bufferIt.WriteU8 (i.ReadU8 ());
|
||||
}
|
||||
length += count;
|
||||
|
||||
if (i.IsEnd () || (i.PeekU8 () != IE_FRAGMENT))
|
||||
{
|
||||
break;
|
||||
}
|
||||
i.Next (1); // skip the Element ID byte
|
||||
count = i.ReadU8 (); // length of the next fragment
|
||||
|
||||
buffer.AddAtEnd (count);
|
||||
bufferIt = buffer.Begin ();
|
||||
bufferIt.Next (length);
|
||||
}
|
||||
|
||||
DeserializeInformationField (buffer.Begin (), length);
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
@@ -188,7 +188,9 @@ typedef uint8_t WifiInformationElementId;
|
||||
#define IE_REDUCED_NEIGHBOR_REPORT ((WifiInformationElementId)201)
|
||||
// TODO Add 202 to 220. See Table 9-92 of 802.11-2020
|
||||
#define IE_VENDOR_SPECIFIC ((WifiInformationElementId)221)
|
||||
// 222 to 254 are reserved
|
||||
// TODO Add 222 to 241. See Table 9-92 of 802.11-2020
|
||||
#define IE_FRAGMENT ((WifiInformationElementId)242)
|
||||
// 243 to 254 are reserved
|
||||
#define IE_EXTENSION ((WifiInformationElementId)255)
|
||||
|
||||
#define IE_EXT_HE_CAPABILITIES ((WifiInformationElementId)35)
|
||||
@@ -233,6 +235,12 @@ typedef uint8_t WifiInformationElementId;
|
||||
* Length field specifies the number of octets in the Information
|
||||
* field.
|
||||
*
|
||||
* Fragmentation of an Information Element is handled transparently by the base
|
||||
* class. Subclasses can simply serialize/deserialize their data into/from a
|
||||
* single large buffer. It is the base class that takes care of splitting
|
||||
* serialized data into multiple fragments (when serializing) or reconstructing
|
||||
* data from multiple fragments when deserializing.
|
||||
*
|
||||
* This class is pure virtual and acts as base for classes which know
|
||||
* how to serialize specific IEs.
|
||||
*/
|
||||
@@ -241,7 +249,8 @@ class WifiInformationElement : public SimpleRefCount<WifiInformationElement>
|
||||
public:
|
||||
virtual ~WifiInformationElement ();
|
||||
/**
|
||||
* Serialize entire IE including Element ID and length fields
|
||||
* Serialize entire IE including Element ID and length fields. Handle
|
||||
* fragmentation of the IE if needed.
|
||||
*
|
||||
* \param i an iterator which points to where the IE should be written.
|
||||
*
|
||||
@@ -249,10 +258,10 @@ public:
|
||||
*/
|
||||
Buffer::Iterator Serialize (Buffer::Iterator i) const;
|
||||
/**
|
||||
* Deserialize entire IE, which must be present. The iterator
|
||||
* passed in must be pointing at the Element ID (i.e., the very
|
||||
* first octet) of the correct type of information element,
|
||||
* otherwise this method will generate a fatal error.
|
||||
* Deserialize entire IE (which may possibly be fragmented into multiple
|
||||
* elements), which must be present. The iterator passed in must be pointing
|
||||
* at the Element ID (i.e., the very first octet) of the correct type of
|
||||
* information element, otherwise this method will generate a fatal error.
|
||||
*
|
||||
* \param i an iterator which points to where the IE should be read.
|
||||
*
|
||||
@@ -260,7 +269,8 @@ public:
|
||||
*/
|
||||
Buffer::Iterator Deserialize (Buffer::Iterator i);
|
||||
/**
|
||||
* Deserialize an IE that is optionally present. The iterator passed in
|
||||
* Deserialize an entire IE (which may possibly be fragmented into multiple
|
||||
* elements) that is optionally present. The iterator passed in
|
||||
* must be pointing at the Element ID of an information element. If
|
||||
* the Element ID is not the requested one, the same iterator will
|
||||
* be returned. Otherwise, an iterator pointing to the octet after
|
||||
@@ -279,7 +289,7 @@ public:
|
||||
Args&&... args);
|
||||
/**
|
||||
* Get the size of the serialized IE including Element ID and
|
||||
* length fields.
|
||||
* length fields (for every element this IE is possibly fragmented into).
|
||||
*
|
||||
* \return the size of the serialized IE in bytes
|
||||
*/
|
||||
@@ -318,7 +328,8 @@ public:
|
||||
|
||||
private:
|
||||
/**
|
||||
* Deserialize entire IE if it is present. The iterator passed in
|
||||
* Deserialize entire IE (which may possibly be fragmented into multiple
|
||||
* elements) if it is present. The iterator passed in
|
||||
* must be pointing at the Element ID of an information element. If
|
||||
* the Element ID is not the one that the given class is interested
|
||||
* in then it will return the same iterator.
|
||||
@@ -328,11 +339,28 @@ private:
|
||||
* \return an iterator
|
||||
*/
|
||||
Buffer::Iterator DeserializeIfPresent (Buffer::Iterator i);
|
||||
/**
|
||||
* Serialize an IE that needs to be fragmented.
|
||||
*
|
||||
* \param i an iterator which points to where the IE should be written.
|
||||
* \param size the size of the body of the IE
|
||||
* \return an iterator pointing to past the IE that was serialized
|
||||
*/
|
||||
Buffer::Iterator SerializeFragments (Buffer::Iterator i, uint16_t size) const;
|
||||
/**
|
||||
* Deserialize the Information field of an IE. Also handle the case in which
|
||||
* the IE is fragmented.
|
||||
*
|
||||
* \param i an iterator which points to where the Information field should be read.
|
||||
* \param length the expected number of bytes to read
|
||||
* \return an iterator pointing to past the IE that was deserialized
|
||||
*/
|
||||
Buffer::Iterator DoDeserialize (Buffer::Iterator i, uint16_t length);
|
||||
/**
|
||||
* Length of serialized information (i.e., the length of the body
|
||||
* of the IE, not including the Element ID and length octets. This
|
||||
* is the value that will appear in the second octet of the entire
|
||||
* IE - the length field)
|
||||
* IE - the length field - if the IE is not fragmented)
|
||||
*
|
||||
* \return the length of serialized information
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user