Skip to content

Commit 000f908

Browse files
802.11: document and test HT response constraints
1 parent b6a3e07 commit 000f908

10 files changed

Lines changed: 465 additions & 12 deletions

File tree

WHATSNEW

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ Notable backward incompatible changes are the following:
119119
These changes are backward incompatible for C++ code that directly references
120120
the old combined ICMP error indication or tag types.
121121

122+
7. 802.11n control-response rate selection
123+
124+
Ordinary ACK and Basic BlockAck responses in the n(mixed-2.4Ghz) profile now
125+
use the mandatory non-HT fallback because BSSBasicRateSet is not modelled.
126+
This can change airtime, timeout trajectories, throughput, and fingerprints
127+
in existing simulations. An HT RTS continues to receive an HT-mixed CTS.
128+
122129
Notable backward compatible changes are the following:
123130

124131
1. IPv6 network configurator

src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ namespace ieee80211 {
1616

1717
using namespace inet::physicallayer;
1818

19+
static const IIeee80211Mode *resolveConfiguredResponseCtsFrameMode(const Ieee80211ModeSet *modeSet, double bitrate, const char *modulePath)
20+
{
21+
if (bitrate == -1)
22+
return nullptr;
23+
try {
24+
return modeSet->getMode(bps(bitrate));
25+
}
26+
catch (const cRuntimeError& error) {
27+
throw cRuntimeError("%s has invalid responseCtsFrameBitrate=%g bps for operation mode '%s'; "
28+
"the configured CTS rate must resolve to a selectable mode (HT RTS responses require an HT mode): %s",
29+
modulePath, bitrate, modeSet->getName(), error.getFormattedMessage().c_str());
30+
}
31+
}
32+
1933
Define_Module(QosRateSelection);
2034

2135
void QosRateSelection::initialize(int stage)
@@ -46,7 +60,7 @@ void QosRateSelection::resolveConfiguredModes(const Ieee80211ModeSet *newModeSet
4660
double responseBlockAckFrameBitrate = par("responseBlockAckFrameBitrate");
4761
auto newResponseBlockAckFrameMode = responseBlockAckFrameBitrate == -1 ? nullptr : newModeSet->getMode(bps(responseBlockAckFrameBitrate));
4862
double responseCtsFrameBitrate = par("responseCtsFrameBitrate");
49-
auto newResponseCtsFrameMode = responseCtsFrameBitrate == -1 ? nullptr : newModeSet->getMode(bps(responseCtsFrameBitrate));
63+
auto newResponseCtsFrameMode = resolveConfiguredResponseCtsFrameMode(newModeSet, responseCtsFrameBitrate, getFullPath().c_str());
5064
auto newFastestMandatoryMode = newModeSet->getFastestMandatoryMode();
5165

5266
modeSet = newModeSet;

src/inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.ned

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ simple QosRateSelection extends SimpleModule
3434
// a configured HT bitrate is an upper bound mapped to the highest mandatory
3535
// non-HT rate at or below it; it does not select an exact HT BlockAck PPDU.
3636
double responseBlockAckFrameBitrate @unit(bps) = default(-1bps);
37-
// -1 selects the standard-derived automatic response. A configured HT value is a deliberate
38-
// model override translated only to HT-mixed while preserving the resolved configured mode's
39-
// MCS, bandwidth, NSS, and GI; it can therefore bypass IEEE 802.11-2024
40-
// 10.6.6.5.3/10.6.6.5.7 response constraints.
37+
// -1 selects the standard-derived automatic response. A configured value must resolve to a
38+
// selectable mode; for an HT RTS it must be HT and is translated to the corresponding HT-mixed
39+
// CTS while preserving MCS, bandwidth, NSS, and GI. An explicitly configured HT value is a
40+
// deliberate override and may bypass IEEE 802.11-2024 10.6.6.5.3/10.6.6.5.7 response constraints.
4141
double responseCtsFrameBitrate @unit(bps) = default(-1bps);
4242

4343
double dataFrameBitrate @unit(bps) = default(-1bps); // Fastest

src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.cc

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ namespace ieee80211 {
2020

2121
using namespace inet::physicallayer;
2222

23+
static const IIeee80211Mode *resolveConfiguredResponseCtsFrameMode(const Ieee80211ModeSet *modeSet, double bitrate, const char *modulePath)
24+
{
25+
if (bitrate == -1)
26+
return nullptr;
27+
try {
28+
return modeSet->getMode(bps(bitrate));
29+
}
30+
catch (const cRuntimeError& error) {
31+
throw cRuntimeError("%s has invalid responseCtsFrameBitrate=%g bps for operation mode '%s'; "
32+
"the configured CTS rate must resolve to a selectable mode (HT RTS responses require an HT mode): %s",
33+
modulePath, bitrate, modeSet->getName(), error.getFormattedMessage().c_str());
34+
}
35+
}
36+
2337
Define_Module(RateSelection);
2438

2539
void RateSelection::initialize(int stage)
@@ -65,7 +79,7 @@ void RateSelection::resolveConfiguredModes(const Ieee80211ModeSet *newModeSet)
6579
double responseAckFrameBitrate = par("responseAckFrameBitrate");
6680
auto newResponseAckFrameMode = responseAckFrameBitrate == -1 ? nullptr : newModeSet->getMode(bps(responseAckFrameBitrate));
6781
double responseCtsFrameBitrate = par("responseCtsFrameBitrate");
68-
auto newResponseCtsFrameMode = responseCtsFrameBitrate == -1 ? nullptr : newModeSet->getMode(bps(responseCtsFrameBitrate));
82+
auto newResponseCtsFrameMode = resolveConfiguredResponseCtsFrameMode(newModeSet, responseCtsFrameBitrate, getFullPath().c_str());
6983
auto newFastestMandatoryMode = newModeSet->getFastestMandatoryMode();
7084

7185
// Commit only after every configured mode has been resolved, so a failed

src/inet/linklayer/ieee80211/mac/rateselection/RateSelection.ned

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ simple RateSelection extends SimpleModule like IRateSelection
2626
// a configured HT bitrate is an upper bound mapped to the highest mandatory
2727
// non-HT rate at or below it; it does not select an exact HT ACK PPDU.
2828
double responseAckFrameBitrate @unit(bps) = default(-1bps);
29-
// -1 selects the standard-derived automatic response. A configured HT value is a deliberate
30-
// model override translated only to HT-mixed while preserving the resolved configured mode's
31-
// MCS, bandwidth, NSS, and GI; it can therefore bypass IEEE 802.11-2024
32-
// 10.6.6.5.3/10.6.6.5.7 response constraints.
29+
// -1 selects the standard-derived automatic response. A configured value must resolve to a
30+
// selectable mode; for an HT RTS it must be HT and is translated to the corresponding HT-mixed
31+
// CTS while preserving MCS, bandwidth, NSS, and GI. An explicitly configured HT value is a
32+
// deliberate override and may bypass IEEE 802.11-2024 10.6.6.5.3/10.6.6.5.7 response constraints.
3333
double responseCtsFrameBitrate @unit(bps) = default(-1bps);
3434

3535
double dataFrameBitrate @unit(bps) = default(-1bps); // Fastest

src/inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ class INET_API Ieee80211ModeSet : public IPrintableObject, public cObject
5858
const IIeee80211Mode *getMode(int index) { return entries[index].mode; }
5959
bool isMandatory(int index) { return entries[index].isMandatory; }
6060

61+
// containsMode() covers entries selectable as the persistent operating mode
62+
// (for example through Ieee80211Transmitter::setMode()). supportsMode()
63+
// additionally covers immutable PHY capabilities that may be selected per
64+
// packet through Ieee80211ModeReq, such as legacy control responses in a
65+
// 2.4 GHz HT profile.
6166
bool containsMode(const IIeee80211Mode *mode) const { return findModeIndex(mode) != -1; }
6267
bool supportsMode(const IIeee80211Mode *mode) const;
6368
bool getIsMandatory(const IIeee80211Mode *mode) const;
@@ -78,8 +83,12 @@ class INET_API Ieee80211ModeSet : public IPrintableObject, public cObject
7883
const IIeee80211Mode *getSlowerMandatoryMode(const IIeee80211Mode *mode) const;
7984
const IIeee80211Mode *getFasterMandatoryMode(const IIeee80211Mode *mode) const;
8085

81-
// Automatic responses follow IEEE 802.11-2024 10.6.6.5.3/10.6.6.5.7. A
82-
// configured HT mode is a deliberate override translated only to HT-mixed.
86+
// Automatic responses follow IEEE 802.11-2024 10.6.6.5.3/10.6.6.5.7.
87+
// A configured CTS mode must be selectable and, for an HT RTS, must be HT;
88+
// it is translated only to the corresponding HT-mixed response. An explicitly
89+
// configured HT mode is a deliberate override and may bypass those response
90+
// constraints. A legacy configured CTS rate is rejected by rate-selection
91+
// initialization, while this API keeps the direct HT/legacy combination fatal.
8392
const IIeee80211Mode *getControlResponseMode(const IIeee80211Mode *mode, const IIeee80211Mode *configuredMode = nullptr) const;
8493
const IIeee80211Mode *getMandatoryControlResponseMode(const IIeee80211Mode *mode) const;
8594
// 2.4 GHz HT modes are converted to non-HT responses. With mandatory=false,
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
%description:
2+
Checks a deterministic n(mixed-2.4Ghz) exchange with HT-mixed RTS/CTS and data
3+
followed by an ERP-OFDM ACK. Basic BlockAck response selection is covered by
4+
the focused public QoS API test because enabling a bounded BAR exchange would
5+
require additional association and agreement setup.
6+
7+
%file: Test.cc
8+
#include <iostream>
9+
10+
#include "inet/common/Simsignals.h"
11+
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
12+
#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ErpOfdmMode.h"
13+
#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211HtMode.h"
14+
#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.h"
15+
#include "inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Transmission.h"
16+
17+
using namespace inet;
18+
using namespace inet::physicallayer;
19+
using namespace inet::ieee80211;
20+
21+
namespace Ieee80211HtMixedRuntime {
22+
23+
static OPP_THREAD_LOCAL int htMixedRtsCount = 0;
24+
static OPP_THREAD_LOCAL int htMixedCtsCount = 0;
25+
static OPP_THREAD_LOCAL int htMixedDataCount = 0;
26+
static OPP_THREAD_LOCAL int erpOfdmAckCount = 0;
27+
28+
class HtMixedCheckingRadio : public Ieee80211Radio, public cListener
29+
{
30+
protected:
31+
virtual void initialize(int stage) override
32+
{
33+
Ieee80211Radio::initialize(stage);
34+
if (stage == INITSTAGE_LOCAL)
35+
subscribe(transmissionStartedSignal, this);
36+
}
37+
38+
virtual void receiveSignal(cComponent *, simsignal_t signalID, cObject *obj, cObject *) override
39+
{
40+
if (signalID != transmissionStartedSignal)
41+
return;
42+
auto transmission = check_and_cast<const Ieee80211Transmission *>(obj);
43+
auto mode = dynamic_cast<const Ieee80211HtMode *>(transmission->getMode());
44+
auto phyHeader = Ieee80211Radio::peekIeee80211PhyHeaderAtFront(transmission->getPacket());
45+
if (transmission->getPreambleDuration() < SIMTIME_ZERO || transmission->getHeaderDuration() < SIMTIME_ZERO || transmission->getDataDuration() < SIMTIME_ZERO ||
46+
transmission->getPreambleDuration() + transmission->getHeaderDuration() + transmission->getDataDuration() != transmission->getDuration())
47+
throw cRuntimeError("Invalid preamble/header/data duration decomposition");
48+
if (mode != nullptr && (transmission->getHeaderDuration() != SIMTIME_ZERO ||
49+
transmission->getDataDuration() != mode->getDataMode()->getDuration(B(phyHeader->getLengthField()))))
50+
throw cRuntimeError("HT transmission did not keep SIG in the preamble and data in the data interval");
51+
auto macHeader = transmission->getPacket()->peekDataAt<Ieee80211MacHeader>(phyHeader->getChunkLength());
52+
bool isHtMixed = mode != nullptr && mode->getPreambleMode()->getPreambleFormat() == Ieee80211HtPreambleMode::HT_PREAMBLE_MIXED;
53+
if (dynamicPtrCast<const Ieee80211RtsFrame>(macHeader) && isHtMixed)
54+
htMixedRtsCount++;
55+
if (dynamicPtrCast<const Ieee80211CtsFrame>(macHeader) && isHtMixed)
56+
htMixedCtsCount++;
57+
if (dynamicPtrCast<const Ieee80211DataHeader>(macHeader) && isHtMixed)
58+
htMixedDataCount++;
59+
if (dynamicPtrCast<const Ieee80211AckFrame>(macHeader) && dynamic_cast<const Ieee80211ErpOfdmMode *>(transmission->getMode()) != nullptr) {
60+
if (transmission->getMode()->getDataMode()->getBandwidth() != MHz(20))
61+
throw cRuntimeError("ERP-OFDM ACK did not use the 20 MHz channel bandwidth");
62+
erpOfdmAckCount++;
63+
}
64+
}
65+
66+
virtual void finish() override
67+
{
68+
Ieee80211Radio::finish();
69+
if (htMixedRtsCount == 0 || htMixedCtsCount == 0 || htMixedDataCount == 0 || erpOfdmAckCount == 0)
70+
throw cRuntimeError("Expected HT-mixed RTS/CTS and data with ERP-OFDM ACK response");
71+
std::cout << "Observed " << htMixedRtsCount << " HT-mixed RTS, " << htMixedCtsCount << " HT-mixed CTS, " << htMixedDataCount << " HT-mixed data, and " << erpOfdmAckCount << " ERP-OFDM ACK response(s).\n";
72+
}
73+
};
74+
75+
Define_Module(HtMixedCheckingRadio);
76+
77+
} // namespace Ieee80211HtMixedRuntime
78+
79+
%file: test.ned
80+
import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator;
81+
import inet.node.inet.AdhocHost;
82+
import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211Radio;
83+
import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium;
84+
85+
module HtMixedCheckingRadio extends Ieee80211Radio
86+
{
87+
parameters:
88+
@class(HtMixedCheckingRadio);
89+
}
90+
91+
network TestHtMixedRuntime
92+
{
93+
submodules:
94+
configurator: Ipv4NetworkConfigurator;
95+
radioMedium: Ieee80211ScalarRadioMedium;
96+
host[2]: AdhocHost;
97+
}
98+
99+
%inifile: omnetpp.ini
100+
[General]
101+
include ../../../../examples/wireless/lan80211/omnetpp-ht-greenfield.ini
102+
network = TestHtMixedRuntime
103+
abstract = false
104+
ned-path = .;../../../../src;../../../../examples;../../lib
105+
cmdenv-express-mode = true
106+
record-vector-results = false
107+
record-eventlog = false
108+
seed-set = 0
109+
**.wlan[*].radio.typename = "HtMixedCheckingRadio"
110+
111+
[Config HtMixedRuntime]
112+
abstract = false
113+
network = TestHtMixedRuntime
114+
sim-time-limit = 2s
115+
*.host[0].numApps = 1
116+
*.host[0].app[0].typename = "UdpBasicApp"
117+
*.host[0].app[0].destAddresses = "host[1]"
118+
*.host[0].app[0].destPort = 1000
119+
*.host[0].app[0].messageLength = 100B
120+
*.host[0].app[0].startTime = 100ms
121+
*.host[0].app[0].sendInterval = 100ms
122+
*.host[1].numApps = 1
123+
*.host[1].app[0].typename = "UdpSink"
124+
*.host[1].app[0].localPort = 1000
125+
**.wlan[*].opMode = "n(mixed-2.4Ghz)"
126+
**.wlan[*].mac.dcf.rtsPolicy.rtsThreshold = 1B
127+
128+
%extraargs: -c HtMixedRuntime
129+
130+
%contains-regex: stdout
131+
Observed [1-9][0-9]* HT-mixed RTS, [1-9][0-9]* HT-mixed CTS, [1-9][0-9]* HT-mixed data, and [1-9][0-9]* ERP-OFDM ACK response\(s\).
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
%description:
2+
Checks that a non-selectable legacy responseCtsFrameBitrate is rejected during
3+
rate-selection resolution for both DCF and QoS selections and both 2.4 GHz HT profiles.
4+
5+
%file: Test.cc
6+
#include <iostream>
7+
#include <cstdio>
8+
#include <string>
9+
10+
#include "inet/common/InitStages.h"
11+
#include "inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h"
12+
#include "inet/linklayer/ieee80211/mac/rateselection/RateSelection.h"
13+
#include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211ModeSet.h"
14+
15+
using namespace inet;
16+
using namespace inet::ieee80211;
17+
using namespace inet::physicallayer;
18+
19+
namespace Ieee80211InvalidConfiguredCtsRate {
20+
21+
template<typename Selector>
22+
static void checkRejected(const char *selectorName, Selector& selector)
23+
{
24+
double bitrate = selector.par("responseCtsFrameBitrate");
25+
char formattedBitrate[64];
26+
std::snprintf(formattedBitrate, sizeof(formattedBitrate), "%g", bitrate);
27+
for (const char *profile : {"n(mixed-2.4Ghz)", "n(greenfield-2.4Ghz)"}) {
28+
bool rejected = false;
29+
try {
30+
selector.resolveConfiguredModesForTest(Ieee80211ModeSet::getModeSet(profile));
31+
}
32+
catch (const cRuntimeError& error) {
33+
rejected = true;
34+
auto message = error.getFormattedMessage();
35+
if (message.find(std::string("responseCtsFrameBitrate=") + formattedBitrate) == std::string::npos ||
36+
message.find("operation mode '" + std::string(profile) + "'") == std::string::npos ||
37+
message.find("selectable mode") == std::string::npos)
38+
throw cRuntimeError("%s diagnostic for %s at %g bps is incomplete: %s", selectorName, profile, bitrate, message.c_str());
39+
}
40+
if (!rejected)
41+
throw cRuntimeError("%s accepted legacy CTS bitrate %g bps in %s", selectorName, bitrate, profile);
42+
}
43+
}
44+
45+
class TestRateSelection : public RateSelection
46+
{
47+
public:
48+
void resolveConfiguredModesForTest(const Ieee80211ModeSet *modeSet) { resolveConfiguredModes(modeSet); }
49+
50+
protected:
51+
virtual void initialize(int stage) override
52+
{
53+
if (stage == INITSTAGE_LINK_LAYER) {
54+
checkRejected("DCF RateSelection", *this);
55+
std::cout << "DCF rejected configured legacy responseCtsFrameBitrate for both HT profiles.\n";
56+
}
57+
}
58+
};
59+
60+
class TestQosRateSelection : public QosRateSelection
61+
{
62+
public:
63+
void resolveConfiguredModesForTest(const Ieee80211ModeSet *modeSet) { resolveConfiguredModes(modeSet); }
64+
65+
protected:
66+
virtual void initialize(int stage) override
67+
{
68+
if (stage == INITSTAGE_LINK_LAYER) {
69+
checkRejected("QoS RateSelection", *this);
70+
std::cout << "QoS rejected configured legacy responseCtsFrameBitrate for both HT profiles.\n";
71+
}
72+
}
73+
};
74+
75+
Define_Module(TestRateSelection);
76+
Define_Module(TestQosRateSelection);
77+
78+
} // namespace Ieee80211InvalidConfiguredCtsRate
79+
80+
%file: test.ned
81+
import inet.linklayer.ieee80211.mac.rateselection.QosRateSelection;
82+
import inet.linklayer.ieee80211.mac.rateselection.RateSelection;
83+
84+
simple TestRateSelection extends RateSelection
85+
{
86+
parameters:
87+
@class(TestRateSelection);
88+
rateControlModule = "";
89+
responseCtsFrameBitrate = default(6Mbps);
90+
}
91+
92+
simple TestQosRateSelection extends QosRateSelection
93+
{
94+
parameters:
95+
@class(TestQosRateSelection);
96+
rateControlModule = "";
97+
responseCtsFrameBitrate = default(6Mbps);
98+
}
99+
100+
simple TestRateSelection24 extends TestRateSelection
101+
{
102+
parameters:
103+
responseCtsFrameBitrate = default(24Mbps);
104+
}
105+
106+
simple TestQosRateSelection24 extends TestQosRateSelection
107+
{
108+
parameters:
109+
responseCtsFrameBitrate = default(24Mbps);
110+
}
111+
112+
network TestInvalidConfiguredCtsRate
113+
{
114+
submodules:
115+
dcfRateSelection6: TestRateSelection;
116+
dcfRateSelection24: TestRateSelection24;
117+
qosRateSelection6: TestQosRateSelection;
118+
qosRateSelection24: TestQosRateSelection24;
119+
}
120+
121+
%inifile: omnetpp.ini
122+
[General]
123+
network = TestInvalidConfiguredCtsRate
124+
ned-path = .;../../../../src;../../lib
125+
cmdenv-express-mode = true
126+
record-vector-results = false
127+
seed-set = 0
128+
129+
%contains: stdout
130+
DCF rejected configured legacy responseCtsFrameBitrate for both HT profiles.
131+
%contains: stdout
132+
QoS rejected configured legacy responseCtsFrameBitrate for both HT profiles.

0 commit comments

Comments
 (0)