Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 54 additions & 11 deletions tests/protocol/wifi/11n/N_HtCapabilitiesIe.test
Original file line number Diff line number Diff line change
@@ -1,27 +1,69 @@
%# expected-result: FAIL
%description:
802.11n HT Capabilities information element in Association Request (type 0): an
Ieee80211HtCapabilities chunk is present in the frame. [NOT-MODELED -- INET's
Ieee80211MgmtFrame.msg defines AssociationRequestFrame with only SSID and
supportedRates fields; no HT Capabilities or HT Operation IE chunk is generated;
the classes Ieee80211HtCapabilities and Ieee80211HtOperation do not exist in INET]
802.11n HT Capabilities information element in an Association Request (type 0).
The n(mixed-2.4Ghz) fixture must advertise HT through the actual management-frame
body, include a meaningful mandatory MCS bit, and carry the HT Capabilities element
on the serialized wire. [Required -- IEEE 802.11-2024 Section 9.4.2.54]

%file: N_HtCapabilitiesIe.cc

#include "ProtocolTest.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h"

#include <cstddef>
#include <cstdint>

namespace inet {
namespace protocoltest {

// Spec-required assertion: an Association Request from an HT STA must include the
// HT Capabilities element (IEEE 802.11-2020 Section 9.4.2.56). INET's mgmt layer
// never inserts this chunk; the filter will never match.
static bool hasHtCapabilitiesMcs0InModel(const Packet *packet)
{
const auto& macHeader = packet->peekAtFront<ieee80211::Ieee80211MacHeader>();
const auto& body = packet->peekAt<ieee80211::Ieee80211AssociationRequestFrame>(macHeader->getChunkLength());
return body->getHtCapabilitiesPresent() && body->getHtCapabilities().rxMcsSupported[0];
}

// The packetSentToLower event is still in the packet domain, so checking the typed
// management body alone would not prove the bytes produced by the serializer. Parse
// the serialized Association Request body as information elements. The
// EID (45) and fixed body length (26) are the HT Capabilities element; MCS 0 is
// bit 0 of the first byte of the 16-octet Supported MCS Set (body offset 3).
static bool hasHtCapabilitiesElementOnWire(const Packet *packet)
{
const auto bytes = packet->peekAllAsBytes()->getBytes();
const auto& macHeader = packet->peekAtFront<ieee80211::Ieee80211MacHeader>();
const size_t headerLength = macHeader->getChunkLength().get<B>();
constexpr size_t fixedAssociationRequestLength = 4; // Capability + Listen Interval
constexpr size_t fcsLength = 4;
if (headerLength + fixedAssociationRequestLength + fcsLength > bytes.size())
return false;
size_t offset = headerLength + fixedAssociationRequestLength;
const size_t bodyEnd = bytes.size() - fcsLength;
while (offset + 2 <= bodyEnd) {
const uint8_t elementId = bytes[offset++];
const size_t length = bytes[offset++];
if (offset + length > bodyEnd)
return false;
if (elementId == 45)
return length == 26 && (bytes[offset + 3] & 1) != 0;
offset += length;
}
return false;
}

// The association request must enter the real STA management path with the HT
// capability gate enabled. The assertion is deliberately separate from the
// selector: if the IE disappears, the first Association Request is selected and
// the test fails immediately instead of waiting for a later matching event.
Define_ProtocolTest(wifi_11n_ht_capabilities_ie)
{
return ProtocolTest("wifi_11n_ht_capabilities_ie")
.once(on("sta1.wlan[0].mac").signal("packetSentToLower")
.filterPacket("ieee80211mac.type == 0 && Ieee80211HtCapabilities.length >= 0")
.describe("an Association Request containing HT Capabilities IE").within(2.5));
.filterPacket("ieee80211mac.type == 0")
.assertPacket("Ieee80211AssociationRequestFrame.htCapabilitiesPresent == true")
.assertEvent([](const MatchContext& c) { return hasHtCapabilitiesMcs0InModel(c.event.packet) && hasHtCapabilitiesElementOnWire(c.event.packet); })
.describe("an HT Association Request with MCS 0 and a serialized HT Capabilities IE")
.within(2.5));
}

} // namespace protocoltest
Expand All @@ -33,6 +75,7 @@ Define_ProtocolTest(wifi_11n_ht_capabilities_ie)
*.tester.testName = "wifi_11n_ht_capabilities_ie"
include ../../ini/_n.ini
include ../../ini/_base.ini
*.sta1.wlan[0].mac.fcsMode = "computed"

%contains: stdout
PROTOCOLTEST wifi_11n_ht_capabilities_ie: PASS
76 changes: 76 additions & 0 deletions tests/protocol/wifi/11n/N_HtCapabilitiesIeLegacy.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
%description:
An Association Request from a legacy STA must not carry an HT Capabilities
information element. The fixture keeps the AP and sta1 in n(mixed-2.4Ghz) mode,
while sta2 is explicitly g(mixed), so the negative observation reaches the same
production management serializer with the HT capability gate disabled.

%file: N_HtCapabilitiesIeLegacy.cc

#include "ProtocolTest.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h"

#include <cstddef>
#include <cstdint>

namespace inet {
namespace protocoltest {

static bool hasHtCapabilitiesInModel(const Packet *packet)
{
const auto& macHeader = packet->peekAtFront<ieee80211::Ieee80211MacHeader>();
const auto& body = packet->peekAt<ieee80211::Ieee80211AssociationRequestFrame>(macHeader->getChunkLength());
return body->getHtCapabilitiesPresent();
}

static bool hasHtCapabilitiesElementOnWire(const Packet *packet)
{
const auto bytes = packet->peekAllAsBytes()->getBytes();
const auto& macHeader = packet->peekAtFront<ieee80211::Ieee80211MacHeader>();
const size_t headerLength = macHeader->getChunkLength().get<B>();
constexpr size_t fixedAssociationRequestLength = 4; // Capability + Listen Interval
constexpr size_t fcsLength = 4;
ASSERT(headerLength + fixedAssociationRequestLength + fcsLength <= bytes.size());
size_t offset = headerLength + fixedAssociationRequestLength;
const size_t bodyEnd = bytes.size() - fcsLength;
while (offset + 2 <= bodyEnd) {
const uint8_t elementId = bytes[offset++];
const size_t length = bytes[offset++];
ASSERT(offset + length <= bodyEnd);
if (elementId == 45)
return true;
offset += length;
}
ASSERT(offset == bodyEnd);
return false;
}

Define_ProtocolTest(wifi_11n_ht_capabilities_ie_legacy)
{
return ProtocolTest("wifi_11n_ht_capabilities_ie_legacy")
.once(on("sta2.wlan[0].mac").signal("packetSentToLower")
.filterPacket("ieee80211mac.type == 0")
.assertPacket("Ieee80211AssociationRequestFrame.htCapabilitiesPresent == false")
.assertEvent([](const MatchContext& c) { return !hasHtCapabilitiesInModel(c.event.packet) && !hasHtCapabilitiesElementOnWire(c.event.packet); })
.describe("a legacy Association Request without HT Capabilities")
.within(2.5));
}

} // namespace protocoltest
} // namespace inet

%inifile: test.ini

[General]
*.tester.testName = "wifi_11n_ht_capabilities_ie_legacy"
include ../../ini/_n.ini
include ../../ini/_base.ini
# Make only the observed STA legacy-capable. The AP and sta1 remain HT-capable,
# which makes this a negative fixture for the same Association Request path.
*.sta2.wlan[0].opMode = "g(mixed)"
*.sta2.wlan[0].mac.modeSet = "g(mixed)"
*.sta2.wlan[0].radio.opMode = "g(mixed)"
*.sta2.wlan[0].mac.fcsMode = "computed"

%contains: stdout
PROTOCOLTEST wifi_11n_ht_capabilities_ie_legacy: PASS
49 changes: 40 additions & 9 deletions tests/protocol/wifi/11n/N_HtOperationIe.test
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
%# expected-result: FAIL
%description:
802.11n HT Operation information element in Beacon (type 8): an Ieee80211HtOperation
chunk is present in the Beacon frame. [NOT-MODELED -- INET's Ieee80211BeaconFrame
contains only SSID, supportedRates, beaconInterval, channelNumber, and
handoverParameters; no HT Operation IE is generated]
An HT AP's generated Beacon carries HT Operation in its management body and on
wire (IEEE 802.11-2024 Section 9.4.2.55). The fixture pins the primary channel
index to zero, corresponding to IEEE channel 1 in the 2.4 GHz band.

%file: N_HtOperationIe.cc

#include "ProtocolTest.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h"

namespace inet {
namespace protocoltest {

// Spec-required assertion: Beacons from an HT AP must include the HT Operation
// element (IEEE 802.11-2020 Section 9.4.2.57). INET omits this element entirely.
static bool hasExpectedHtOperation(const Packet *packet)
{
const auto& header = packet->peekAtFront<ieee80211::Ieee80211MacHeader>();
const auto& body = packet->peekAt<ieee80211::Ieee80211BeaconFrame>(header->getChunkLength());
if (!body->getHtOperationPresent() || body->getHtOperation().primaryChannel != 1)
return false;
const auto bytes = packet->peekAllAsBytes()->getBytes();
size_t offset = header->getChunkLength().get<B>() + 12; // TSF, interval, capability
constexpr size_t fcsLength = 4;
if (offset + fcsLength > bytes.size())
return false;
const size_t end = bytes.size() - fcsLength;
bool found = false;
while (offset + 2 <= end) {
const auto id = bytes[offset++];
const size_t length = bytes[offset++];
if (offset + length > end)
return false;
if (id == 61) {
if (found || length != 22 || bytes[offset] != 1)
return false;
found = true;
}
offset += length;
}
return found && offset == end;
}

Define_ProtocolTest(wifi_11n_ht_operation_ie)
{
return ProtocolTest("wifi_11n_ht_operation_ie")
.once(on("ap.wlan[0].mac").signal("packetSentToLower")
.filterPacket("ieee80211mac.type == 8 && Ieee80211HtOperation.length >= 0")
.describe("a Beacon frame containing HT Operation IE").within(2.5));
.filterPacket("ieee80211mac.type == 8")
.assertPacket("Ieee80211BeaconFrame.htOperationPresent == true")
.assertEvent([](const MatchContext& c) { return hasExpectedHtOperation(c.event.packet); })
.describe("an HT Beacon advertising primary channel 1 in its body and wire IE")
.within(2.5));
}

} // namespace protocoltest
Expand All @@ -31,6 +60,8 @@ Define_ProtocolTest(wifi_11n_ht_operation_ie)
*.tester.testName = "wifi_11n_ht_operation_ie"
include ../../ini/_n.ini
include ../../ini/_base.ini
*.ap.wlan[0].radio.channelNumber = 0
*.ap.wlan[0].mac.fcsMode = "computed"

%contains: stdout
PROTOCOLTEST wifi_11n_ht_operation_ie: PASS
26 changes: 19 additions & 7 deletions tests/protocol/wifi/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Each test is one **`.test`** file: its program lives in a `%file: <Name>.cc`

```sh
. /home/levy/workspace/omnetpp/setenv -q
./run-tests.sh # all 74 tests
./run-tests.sh # all 75 tests
./run-tests.sh 11n/N_BlockAck.test # a subset
```

Expand Down Expand Up @@ -64,7 +64,7 @@ ignores `%#` lines — the opp_repl wrapper reads it). The wrapper then reports
Only `(unexpected)` outcomes fail the run (non-zero exit); expected PASS and expected FAIL are
both green.

**Today: 39 CONFORMS (expected PASS) + 35 NOT-MODELED (expected FAIL), 0 DEVIATES across 74
**Today: 42 CONFORMS (expected PASS) + 33 NOT-MODELED (expected FAIL), 0 DEVIATES across 75
tests — all results as expected, run exits 0.**

## Conformance matrix
Expand Down Expand Up @@ -152,15 +152,27 @@ be satisfied without changing the shared harness.
| `N_CompressedBa` | Compressed Block Ack bitmap | R | ✅ |
| `N_RtsCts` | HCF RTS/CTS | R | ✅ |
| `N_Ampdu` | A-MPDU aggregation | R | ⛔ policy returns nullptr |
| `N_HtCapabilitiesIe` | HT Capabilities IE in AssocReq | R | ⛔ no IE in mgmt frame |
| `N_HtOperationIe` | HT Operation IE in Beacon | R | ⛔ no IE in mgmt frame |
| `N_HtCapabilitiesIe` | HT Capabilities IE in AssocReq | R | ✅ typed body, MCS 0 and wire IE |
| `N_HtCapabilitiesIeLegacy` | Legacy AssocReq omits HT Capabilities | R | ✅ typed body and wire absence |
| `N_HtOperationIe` | HT Operation IE in Beacon | R | ✅ typed body and wire primary channel |
| `N_Smps` | SM Power Save action frame | O | ⛔ |
| `N_Greenfield` | HT greenfield preamble | O | ⛔ always MIXED |
| `N_StbcCap` | HT STBC | O | ⛔ `getSTBC()` hardcoded 0 |
| `N_LdpcCap` | HT LDPC | O | ⛔ LDPC absent |
| `N_2040Coex` | 20/40 BSS Coexistence | O | ⛔ |
| `N_ReverseDirection` | Reverse Direction Grant (RDG) | O | ⛔ no HT Control field |

The three HT management-element cases above pass in debug mode on the audit implementation
based on `7287f347aaca36e557c708d6930a6f5a450b2833`:

```sh
inet_run_protocol_tests -m debug -f '(N_HtCapabilitiesIe|N_HtCapabilitiesIeLegacy|N_HtOperationIe)\.test'
```

The selectors observe the first relevant production frame and assert the element separately,
so an omitted element fails the assertion instead of being filtered out. This verifies these
frames and fields, not every capability-negotiation procedure.

### 11ac (WiFi 5 — VHT)
| Test | Feature | R/O | |
|------|---------|-----|--|
Expand All @@ -185,9 +197,9 @@ be satisfied without changing the shared harness.
aggregation-policy module exist, but `BasicMpduAggregationPolicy::computeAggregateFrames()`
returns `nullptr`, so no A-MPDU is ever emitted — a *required* 802.11ac feature (all VHT data
is meant to ride in A-MPDUs). The most significant gap. (A-MSDU *is* modeled.)
- **No HT/VHT management Information Elements** (`N_HtCapabilitiesIe`, `N_HtOperationIe`,
`Ac_VhtIe`): Beacon/AssociationRequest carry no HT/VHT Capabilities or Operation elements, so
capability negotiation is not modeled — notable for real-device interoperability.
- **VHT management Information Elements** (`Ac_VhtIe`) remain a separately tracked gap.
HT Capabilities and HT Operation are modeled and checked in generated management frames;
their test results do not establish VHT support.
- **ERP protection absent** (`G_ErpProtection`): `g(mixed)` never emits CTS-to-self/RTS before
ERP-OFDM data when legacy STAs are present.
- **STBC hardcoded off** in both HT and VHT signal modes (`getSTBC()` ≡ 0); **LDPC absent**.
Expand Down