From 2d048b1cf053f3273493f7712c80a42e5ece7094 Mon Sep 17 00:00:00 2001 From: Stefano Avallone Date: Wed, 13 Jul 2022 18:08:05 +0200 Subject: [PATCH] wifi: Add support for IE fragmentation --- RELEASE_NOTES.md | 1 + src/wifi/model/wifi-information-element.cc | 159 ++++++++++++++++++--- src/wifi/model/wifi-information-element.h | 48 +++++-- 3 files changed, 177 insertions(+), 31 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 1df21fb98..733ee1d9d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -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 diff --git a/src/wifi/model/wifi-information-element.cc b/src/wifi/model/wifi-information-element.cc index 9b01129a6..11a4dcc53 100644 --- a/src/wifi/model/wifi-information-element.cc +++ b/src/wifi/model/wifi-information-element.cc @@ -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; } diff --git a/src/wifi/model/wifi-information-element.h b/src/wifi/model/wifi-information-element.h index 0a80996c3..2b122ac38 100644 --- a/src/wifi/model/wifi-information-element.h +++ b/src/wifi/model/wifi-information-element.h @@ -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 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 */