wifi: Add support for IE fragmentation

This commit is contained in:
Stefano Avallone
2022-07-13 18:08:05 +02:00
parent def20b0276
commit 2d048b1cf0
3 changed files with 177 additions and 31 deletions

View File

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

View File

@@ -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;
}

View File

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