A Discrete-Event Network Simulator
API
wifi-ie-fragment-test.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2022 Universita' degli Studi di Napoli Federico II
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * Author: Stefano Avallone <stavallo@unina.it>
18  */
19 
20 #include "ns3/header-serialization-test.h"
21 #include "ns3/log.h"
22 #include "ns3/supported-rates.h"
23 #include "ns3/wifi-information-element.h"
24 #include "ns3/wifi-mgt-header.h"
25 
26 #include <list>
27 #include <numeric>
28 #include <optional>
29 
30 using namespace ns3;
31 
32 NS_LOG_COMPONENT_DEFINE("WifiIeFragmentTest");
33 
35 static bool g_extendedIe = false;
36 
45 {
46  public:
47  TestWifiSubElement() = default;
48 
56  TestWifiSubElement(uint16_t count, uint8_t start);
57 
58  WifiInformationElementId ElementId() const override;
59 
60  private:
61  uint16_t GetInformationFieldSize() const override;
62  void SerializeInformationField(Buffer::Iterator start) const override;
63  uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
64 
65  std::list<uint8_t> m_content;
66 };
67 
69 {
70  NS_LOG_FUNCTION(this << count << +start);
71  m_content.resize(count);
72  std::iota(m_content.begin(), m_content.end(), start);
73 }
74 
77 {
78  return 0;
79 }
80 
81 uint16_t
83 {
84  return m_content.size();
85 }
86 
87 void
89 {
90  NS_LOG_FUNCTION(this);
91  for (const auto& byte : m_content)
92  {
93  start.WriteU8(byte);
94  }
95 }
96 
97 uint16_t
99 {
100  NS_LOG_FUNCTION(this << length);
101  m_content.clear();
102  for (uint16_t i = 0; i < length; i++)
103  {
104  m_content.push_back(start.ReadU8());
105  }
106  return length;
107 }
108 
117 {
118  public:
124 
125  WifiInformationElementId ElementId() const override;
126  WifiInformationElementId ElementIdExt() const override;
132  void AddSubelement(TestWifiSubElement&& subelement);
133 
134  private:
135  uint16_t GetInformationFieldSize() const override;
136  void SerializeInformationField(Buffer::Iterator start) const override;
137  uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override;
138 
139  bool m_extended;
140  std::list<TestWifiSubElement> m_content;
141 };
142 
144  : m_extended(extended)
145 {
146  NS_LOG_FUNCTION(this << extended);
147 }
148 
151 {
152  return m_extended ? 255 : 2; // reserved in 802.11-2020
153 }
154 
157 {
159  return 32; // reserved in 802.11-2020
160 }
161 
162 void
164 {
165  NS_LOG_FUNCTION(this);
166  m_content.push_back(std::move(subelement));
167 }
168 
169 uint16_t
171 {
172  uint16_t size = (m_extended ? 1 : 0);
173  for (const auto& subelement : m_content)
174  {
175  size += subelement.GetSerializedSize();
176  }
177  return size;
178 }
179 
180 void
182 {
183  NS_LOG_FUNCTION(this);
184  for (const auto& subelement : m_content)
185  {
186  start = subelement.Serialize(start);
187  }
188 }
189 
190 uint16_t
192 {
193  NS_LOG_FUNCTION(this << length);
194 
196  uint16_t count = 0;
197 
198  while (count < length)
199  {
200  TestWifiSubElement subelement;
201  i = subelement.Deserialize(i);
202  m_content.push_back(std::move(subelement));
203  count = i.GetDistanceFrom(start);
204  }
205  return count;
206 }
207 
215  : public WifiMgtHeader<TestHeader, std::tuple<std::vector<TestWifiInformationElement>>>
216 {
217  friend class WifiMgtHeader<TestHeader, std::tuple<std::vector<TestWifiInformationElement>>>;
218 
219  public:
220  ~TestHeader() override = default;
221 
226  static TypeId GetTypeId();
227 
231  TypeId GetInstanceTypeId() const override;
232 
233  private:
238  void InitForDeserialization(std::optional<TestWifiInformationElement>& optElem);
239 };
240 
241 NS_OBJECT_ENSURE_REGISTERED(TestHeader);
242 
245 {
246  static TypeId tid = TypeId("ns3::TestHeader")
247  .SetParent<Header>()
248  .SetGroupName("Wifi")
249  .AddConstructor<TestHeader>();
250  return tid;
251 }
252 
253 TypeId
255 {
256  return GetTypeId();
257 }
258 
259 void
260 TestHeader::InitForDeserialization(std::optional<TestWifiInformationElement>& optElem)
261 {
262  optElem.emplace(g_extendedIe);
263 }
264 
272 {
273  public:
279  ~WifiIeFragmentationTest() override = default;
280 
287  Buffer SerializeIntoBuffer(const WifiInformationElement& element);
288 
296  void CheckSerializedByte(const Buffer& buffer, uint32_t position, uint8_t value);
297 
298  private:
299  void DoRun() override;
300 
301  bool m_extended;
302 };
303 
305  : HeaderSerializationTestCase("Check fragmentation of Information Elements"),
306  m_extended(extended)
307 {
308 }
309 
310 Buffer
312 {
313  Buffer buffer;
314  buffer.AddAtStart(element.GetSerializedSize());
315  element.Serialize(buffer.Begin());
316  return buffer;
317 }
318 
319 void
320 WifiIeFragmentationTest::CheckSerializedByte(const Buffer& buffer, uint32_t position, uint8_t value)
321 {
322  Buffer::Iterator it = buffer.Begin();
323  it.Next(position);
324  uint8_t byte = it.ReadU8();
325  NS_TEST_EXPECT_MSG_EQ(+byte, +value, "Unexpected byte at pos=" << position);
326 }
327 
328 void
330 {
331  // maximum IE size to avoid incurring IE fragmentation
332  uint16_t limit = m_extended ? 254 : 255;
333 
334  TestHeader header;
336 
337  /*
338  * Add an IE (containing 2 subelements). No fragmentation occurs
339  */
340 
341  uint16_t sub01Size = 50;
342  uint16_t sub02Size = limit - sub01Size;
343 
344  auto sub01 =
345  TestWifiSubElement(sub01Size - 2, 53); // minus 2 to account for Subelement ID and Length
346  auto sub02 = TestWifiSubElement(sub02Size - 2, 26);
347 
348  auto testIe = TestWifiInformationElement(m_extended);
349  testIe.AddSubelement(std::move(sub01));
350  testIe.AddSubelement(std::move(sub02));
351 
352  {
353  Buffer buffer = SerializeIntoBuffer(testIe);
354  CheckSerializedByte(buffer, 1, 255); // element length is the maximum length
355  if (m_extended)
356  {
357  CheckSerializedByte(buffer, 2, testIe.ElementIdExt());
358  }
359  CheckSerializedByte(buffer, (m_extended ? 3 : 2), TestWifiSubElement().ElementId());
360  CheckSerializedByte(buffer, (m_extended ? 3 : 2) + 1, sub01Size - 2); // subelement 1 Length
361  CheckSerializedByte(buffer,
362  (m_extended ? 3 : 2) + sub01Size,
363  TestWifiSubElement().ElementId());
364  CheckSerializedByte(buffer,
365  (m_extended ? 3 : 2) + sub01Size + 1,
366  sub02Size - 2); // subelement 2 Length
367  }
368 
369  header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
370  uint32_t expectedHdrSize = 2 + 255;
371  NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
372  TestHeaderSerialization(header);
373 
374  /*
375  * Add an IE (containing 2 subelements) that is fragmented into 2 fragments.
376  * Subelements are not fragmented
377  */
378  sub01Size = 65;
379  sub02Size = limit + 1 - sub01Size;
380 
381  sub01 =
382  TestWifiSubElement(sub01Size - 2, 47); // minus 2 to account for Subelement ID and Length
383  sub02 = TestWifiSubElement(sub02Size - 2, 71);
384 
386  testIe.AddSubelement(std::move(sub01));
387  testIe.AddSubelement(std::move(sub02));
388 
389  {
390  Buffer buffer = SerializeIntoBuffer(testIe);
391  CheckSerializedByte(buffer, 1, 255); // maximum length for first element fragment
392  if (m_extended)
393  {
394  CheckSerializedByte(buffer, 2, testIe.ElementIdExt());
395  }
396  CheckSerializedByte(buffer, (m_extended ? 3 : 2), TestWifiSubElement().ElementId());
397  CheckSerializedByte(buffer, (m_extended ? 3 : 2) + 1, sub01Size - 2); // subelement 1 Length
398  CheckSerializedByte(buffer,
399  (m_extended ? 3 : 2) + sub01Size,
400  TestWifiSubElement().ElementId());
401  CheckSerializedByte(buffer,
402  (m_extended ? 3 : 2) + sub01Size + 1,
403  sub02Size - 2); // subelement 2 Length
404  CheckSerializedByte(buffer, 2 + 255, IE_FRAGMENT); // Fragment ID
405  CheckSerializedByte(buffer,
406  2 + 255 + 1,
407  1); // the length of the second element fragment is 1
408  }
409 
410  header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
411  expectedHdrSize += 2 + 255 // first fragment
412  + 2 + 1; // second fragment
413  NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
414  TestHeaderSerialization(header);
415 
416  /*
417  * Add an IE (containing 3 subelements) that is fragmented into 2 fragments.
418  * Subelements are not fragmented
419  */
420  sub01Size = 200;
421  sub02Size = 200;
422  uint16_t sub03Size = limit + 255 - sub01Size - sub02Size;
423 
424  sub01 =
425  TestWifiSubElement(sub01Size - 2, 16); // minus 2 to account for Subelement ID and Length
426  sub02 = TestWifiSubElement(sub02Size - 2, 83);
427  auto sub03 = TestWifiSubElement(sub03Size - 2, 98);
428 
430  testIe.AddSubelement(std::move(sub01));
431  testIe.AddSubelement(std::move(sub02));
432  testIe.AddSubelement(std::move(sub03));
433 
434  {
435  Buffer buffer = SerializeIntoBuffer(testIe);
436  CheckSerializedByte(buffer, 1, 255); // maximum length for first element fragment
437  if (m_extended)
438  {
439  CheckSerializedByte(buffer, 2, testIe.ElementIdExt());
440  }
441  CheckSerializedByte(buffer, (m_extended ? 3 : 2), TestWifiSubElement().ElementId());
442  CheckSerializedByte(buffer, (m_extended ? 3 : 2) + 1, sub01Size - 2); // subelement 1 Length
443  CheckSerializedByte(buffer,
444  (m_extended ? 3 : 2) + sub01Size,
445  TestWifiSubElement().ElementId());
446  CheckSerializedByte(buffer,
447  (m_extended ? 3 : 2) + sub01Size + 1,
448  sub02Size - 2); // subelement 2 Length
449  CheckSerializedByte(buffer, 2 + 255, IE_FRAGMENT); // Fragment ID
450  CheckSerializedByte(buffer, 2 + 255 + 1, 255); // maximum length for second element fragment
451  }
452 
453  header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
454  expectedHdrSize += 2 + 255 // first fragment
455  + 2 + 255; // second fragment
456  NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
457  TestHeaderSerialization(header);
458 
459  /*
460  * Add an IE (containing 3 subelements) that is fragmented into 3 fragments.
461  * Subelements are not fragmented
462  */
463  sub01Size = 200;
464  sub02Size = 200;
465  sub03Size = limit + 255 + 1 - sub01Size - sub02Size;
466 
467  sub01 =
468  TestWifiSubElement(sub01Size - 2, 20); // minus 2 to account for Subelement ID and Length
469  sub02 = TestWifiSubElement(sub02Size - 2, 77);
470  sub03 = TestWifiSubElement(sub03Size - 2, 14);
471 
473  testIe.AddSubelement(std::move(sub01));
474  testIe.AddSubelement(std::move(sub02));
475  testIe.AddSubelement(std::move(sub03));
476 
477  {
478  Buffer buffer = SerializeIntoBuffer(testIe);
479  CheckSerializedByte(buffer, 1, 255); // maximum length for first element fragment
480  if (m_extended)
481  {
482  CheckSerializedByte(buffer, 2, testIe.ElementIdExt());
483  }
484  CheckSerializedByte(buffer, (m_extended ? 3 : 2), TestWifiSubElement().ElementId());
485  CheckSerializedByte(buffer, (m_extended ? 3 : 2) + 1, sub01Size - 2); // subelement 1 Length
486  CheckSerializedByte(buffer,
487  (m_extended ? 3 : 2) + sub01Size,
488  TestWifiSubElement().ElementId());
489  CheckSerializedByte(buffer,
490  (m_extended ? 3 : 2) + sub01Size + 1,
491  sub02Size - 2); // subelement 2 Length
492  CheckSerializedByte(buffer, 2 + 255, IE_FRAGMENT); // Fragment ID
493  CheckSerializedByte(buffer, 2 + 255 + 1, 255); // maximum length for second fragment
494  CheckSerializedByte(buffer,
495  (m_extended ? 3 : 2) + sub01Size + 2 + sub02Size,
496  TestWifiSubElement().ElementId());
497  CheckSerializedByte(buffer,
498  (m_extended ? 3 : 2) + sub01Size + 2 + sub02Size + 1,
499  sub03Size - 2); // subelement 3 Length
500  CheckSerializedByte(buffer, 2 * (2 + 255), IE_FRAGMENT); // Fragment ID
501  CheckSerializedByte(buffer, 2 * (2 + 255) + 1, 1); // the length of the third fragment is 1
502  }
503 
504  header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
505  expectedHdrSize += 2 + 255 // first fragment
506  + 2 + 255 // second fragment
507  + 2 + 1; // third fragment
508  NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
509  TestHeaderSerialization(header);
510 
511  /*
512  * Add an IE containing one subelement of the maximum size.
513  * The IE is fragmented into 2 fragments.
514  */
515  sub01Size = 2 + 255;
516 
517  sub01 =
518  TestWifiSubElement(sub01Size - 2, 47); // minus 2 to account for Subelement ID and Length
519 
521  testIe.AddSubelement(std::move(sub01));
522 
523  {
524  Buffer buffer = SerializeIntoBuffer(testIe);
525  CheckSerializedByte(buffer, 1, 255); // maximum length for first element fragment
526  if (m_extended)
527  {
528  CheckSerializedByte(buffer, 2, testIe.ElementIdExt());
529  }
530  CheckSerializedByte(buffer, (m_extended ? 3 : 2), TestWifiSubElement().ElementId());
531  CheckSerializedByte(buffer, (m_extended ? 3 : 2) + 1, sub01Size - 2); // subelement 1 Length
532  CheckSerializedByte(buffer, 2 + 255, IE_FRAGMENT); // Fragment ID
533  CheckSerializedByte(buffer,
534  2 + 255 + 1,
535  (m_extended ? 3 : 2)); // length of the second element fragment
536  }
537 
538  header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
539  expectedHdrSize += 2 + 255 // first fragment
540  + 2 + (m_extended ? 3 : 2); // second fragment
541  NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
542  TestHeaderSerialization(header);
543 
544  /*
545  * Add an IE containing one subelement that gets fragmented.
546  * The IE is fragmented into 2 fragments as well.
547  */
548  sub01Size = 2 + 256;
549 
550  sub01 =
551  TestWifiSubElement(sub01Size - 2, 84); // minus 2 to account for Subelement ID and Length
552 
554  testIe.AddSubelement(std::move(sub01));
555 
556  {
557  Buffer buffer = SerializeIntoBuffer(testIe);
558  CheckSerializedByte(buffer, 1, 255); // maximum length for first element fragment
559  if (m_extended)
560  {
561  CheckSerializedByte(buffer, 2, testIe.ElementIdExt());
562  }
563  CheckSerializedByte(buffer, (m_extended ? 3 : 2), TestWifiSubElement().ElementId());
564  CheckSerializedByte(buffer,
565  (m_extended ? 3 : 2) + 1,
566  255); // first subelement fragment Length
567  CheckSerializedByte(buffer,
568  2 + 255,
569  IE_FRAGMENT); // Fragment ID for second element fragment
570  // Subelement bytes in first element fragment: X = 255 - 1 (Ext ID, if any) - 1 (Sub ID) - 1
571  // (Sub Length) Subelement bytes in second element fragment: Y = 256 - X = (m_extended ? 4 :
572  // 3) Length of the second element fragment: Y + 2 (Fragment ID and Length for second
573  // subelement fragment)
574  CheckSerializedByte(buffer, 2 + 255 + 1, (m_extended ? 6 : 5));
575  CheckSerializedByte(buffer,
576  2 + 255 + 2 + (m_extended ? 3 : 2),
577  IE_FRAGMENT); // Fragment ID for second subelement fragment
578  CheckSerializedByte(buffer,
579  2 + 255 + 2 + (m_extended ? 3 : 2) + 1,
580  1); // Length for second subelement fragment
581  }
582 
583  header.Get<TestWifiInformationElement>().push_back(std::move(testIe));
584  expectedHdrSize += 2 + 255 // first fragment
585  + 2 + (m_extended ? 6 : 5); // second fragment
586  NS_TEST_EXPECT_MSG_EQ(header.GetSerializedSize(), expectedHdrSize, "Unexpected header size");
587  TestHeaderSerialization(header);
588 }
589 
597 {
598  public:
600 };
601 
603  : TestSuite("wifi-ie-fragment", UNIT)
604 {
605  AddTestCase(new WifiIeFragmentationTest(false), TestCase::QUICK);
606  AddTestCase(new WifiIeFragmentationTest(true), TestCase::QUICK);
607 }
608 
Test header that can contain multiple test information elements.
TypeId GetInstanceTypeId() const override
void InitForDeserialization(std::optional< TestWifiInformationElement > &optElem)
static TypeId GetTypeId()
Get the type ID.
Information Element to test IE fragmentation.
uint16_t GetInformationFieldSize() const override
Length of serialized information (i.e., the length of the body of the IE, not including the Element I...
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override
Deserialize information (i.e., the body of the IE, not including the Element ID and length octets)
TestWifiInformationElement(bool extended)
Constructor.
void AddSubelement(TestWifiSubElement &&subelement)
Append the given subelement.
WifiInformationElementId ElementId() const override
Get the wifi information element ID.
WifiInformationElementId ElementIdExt() const override
Get the wifi information element ID extension.
bool m_extended
whether this IE has an Element ID Extension field
std::list< TestWifiSubElement > m_content
content of the IE
void SerializeInformationField(Buffer::Iterator start) const override
Serialize information (i.e., the body of the IE, not including the Element ID and length octets)
Subelement to test fragmentation.
WifiInformationElementId ElementId() const override
Get the wifi information element ID.
TestWifiSubElement()=default
std::list< uint8_t > m_content
content of the IE
void SerializeInformationField(Buffer::Iterator start) const override
Serialize information (i.e., the body of the IE, not including the Element ID and length octets)
uint16_t DeserializeInformationField(Buffer::Iterator start, uint16_t length) override
Deserialize information (i.e., the body of the IE, not including the Element ID and length octets)
uint16_t GetInformationFieldSize() const override
Length of serialized information (i.e., the length of the body of the IE, not including the Element I...
Test fragmentation of Information Elements.
Buffer SerializeIntoBuffer(const WifiInformationElement &element)
Serialize the given element in a buffer.
WifiIeFragmentationTest(bool extended)
Constructor.
bool m_extended
whether the IE includes an Element ID Extension field
void CheckSerializedByte(const Buffer &buffer, uint32_t position, uint8_t value)
Check that the given buffer contains the given value at the given position.
void DoRun() override
Implementation to actually run this TestCase.
~WifiIeFragmentationTest() override=default
wifi Information Element fragmentation Test Suite
iterator in a Buffer instance
Definition: buffer.h:100
uint8_t ReadU8()
Definition: buffer.h:1027
uint32_t GetDistanceFrom(const Iterator &o) const
Definition: buffer.cc:780
void Next()
go forward by one byte
Definition: buffer.h:853
automatically resized byte buffer
Definition: buffer.h:94
void AddAtStart(uint32_t start)
Definition: buffer.cc:314
Buffer::Iterator Begin() const
Definition: buffer.h:1074
Protocol header serialization and deserialization.
Definition: header.h:44
Subclass of TestCase class adding the ability to test the serialization and deserialization of a Head...
void TestHeaderSerialization(const T &hdr, Args &&... args)
Serialize the given header in a buffer, then create a new header by deserializing from the buffer and...
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
A suite of tests to run.
Definition: test.h:1256
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
Information element, as defined in 802.11-2007 standard.
uint16_t GetSerializedSize() const
Get the size of the serialized IE including Element ID and length fields (for every element this IE i...
Buffer::Iterator Deserialize(Buffer::Iterator i)
Deserialize entire IE (which may possibly be fragmented into multiple elements), which must be presen...
Buffer::Iterator Serialize(Buffer::Iterator i) const
Serialize entire IE including Element ID and length fields.
Implement the header for management frames.
#define NS_ABORT_IF(cond)
Abnormal program termination if a condition is true.
Definition: abort.h:76
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
#define NS_TEST_EXPECT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report if not.
Definition: test.h:251
Every class exported by the ns3 library is enclosed in the ns3 namespace.
@ extended
Definition: ff-mac-common.h:85
uint8_t WifiInformationElementId
This type is used to represent an Information Element ID.
value
Definition: second.py:48
static bool g_extendedIe
whether the test Information Element includes an Element ID Extension field
static WifiIeFragmentationTestSuite g_wifiIeFragmentationTestSuite
the test suite
#define IE_FRAGMENT