From 5e41880dacdac559f28b0a126fa62a5d3ff42083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Gonz=C3=A1lez=20L=C3=B3pez?= Date: Wed, 16 Sep 2026 00:23:07 +0200 Subject: [PATCH] ieee80211: fix: reject AP data from unassociated transmitters Validate the original MPDU transmitter before data service or A-MSDU address rewriting, while preserving immediate ACK behavior. Full APs discard unauthorized data; the simplified AP retains its documented all-associated abstraction. Clear authoritative station state on AP shutdown. A typed MAC-to-management indication requests the verified State-2 response: Disassociation reason 7 for an authenticated but unassociated individual transmitter. Do not infer a mandatory State-1 Deauthentication rule from historical drafts. DCF/HCF rejection and associated positive controls, lifecycle tests, and the on-air State-2 response pass. The SignalLevels showcase now rejects one cross-SSID ARP data frame at accessPointB, changing only its tplx trajectory fingerprint from 51ec-d359 to 051b-668f. The other fingerprint ingredients and the neighboring IEEE 802.11 visualizer configurations remain unchanged. Change: src.ieee80211.ap | behavior.change.fix | test | wifi-audit --- .../linklayer/ieee80211/mac/Ieee80211Mac.cc | 22 ++ .../linklayer/ieee80211/mac/Ieee80211Mac.h | 4 + .../ieee80211/mac/coordinationfunction/Dcf.cc | 13 +- .../ieee80211/mac/coordinationfunction/Hcf.cc | 10 + .../mgmt/Ieee80211Class3FrameInd.msg | 16 ++ .../ieee80211/mgmt/Ieee80211MgmtAp.cc | 15 ++ .../ieee80211/mgmt/Ieee80211MgmtAp.h | 1 + .../mgmt/Ieee80211MgmtApSimplified.cc | 7 + .../mgmt/Ieee80211MgmtApSimplified.h | 1 + .../ieee80211/mgmt/Ieee80211MgmtBase.cc | 7 + .../ieee80211/mgmt/Ieee80211MgmtBase.h | 3 + .../linklayer/ieee80211/mib/Ieee80211Mib.h | 2 + tests/fingerprint/showcases.csv | 2 +- tests/module/Ieee80211ApClass3Response_1.test | 252 ++++++++++++++++++ .../Ieee80211ApSourceAdmissionQos_1.test | 244 +++++++++++++++++ .../module/Ieee80211ApSourceAdmission_1.test | 243 +++++++++++++++++ 16 files changed, 840 insertions(+), 2 deletions(-) create mode 100644 src/inet/linklayer/ieee80211/mgmt/Ieee80211Class3FrameInd.msg create mode 100644 tests/module/Ieee80211ApClass3Response_1.test create mode 100644 tests/module/Ieee80211ApSourceAdmissionQos_1.test create mode 100644 tests/module/Ieee80211ApSourceAdmission_1.test diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc index d87f581231f..9570fd2c180 100644 --- a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc @@ -6,6 +6,7 @@ #include "inet/linklayer/ieee80211/mac/Ieee80211Mac.h" +#include "inet/linklayer/ieee80211/mgmt/Ieee80211Class3FrameInd_m.h" #include @@ -200,6 +201,27 @@ void Ieee80211Mac::handleUpperPacket(Packet *packet) processUpperFrame(packet, header); } +void Ieee80211Mac::notifyClass3FrameRejected(const Ptr& header) +{ + Enter_Method("notifyClass3FrameRejected"); + auto indication = new Ieee80211Class3FrameInd("Class3FrameRejected"); + indication->setTransmitterAddress(header->getTransmitterAddress()); + indication->setIndividuallyAddressed(!header->getReceiverAddress().isMulticast()); + send(indication, "mgmtOut"); +} + + +bool Ieee80211Mac::isDataFrameFromAssociatedStation(const Ptr& header) const +{ + // IEEE Std 802.11-2024, 11.3.3 and 11.3.5.1: infrastructure data is Class 3. + if (mib->mode != Ieee80211Mib::INFRASTRUCTURE || mib->bssStationData.stationType != Ieee80211Mib::ACCESS_POINT || + !mib->bssAccessPointData.requireAssociatedTransmitter) + return true; + auto it = mib->bssAccessPointData.stations.find(header->getTransmitterAddress()); + return it != mib->bssAccessPointData.stations.end() && it->second == Ieee80211Mib::ASSOCIATED; +} + + void Ieee80211Mac::handleLowerPacket(Packet *packet) { if (rx->lowerFrameReceived(packet)) { diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h index 1114deaed00..5d2ff3aefae 100644 --- a/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h @@ -107,6 +107,10 @@ class INET_API Ieee80211Mac : public MacProtocolBase virtual void sendDownFrame(Packet *frame); virtual void sendDownPendingRadioConfigMsg(); + // Check original MPDU transmitter identity before recipient data processing. + virtual void notifyClass3FrameRejected(const Ptr& header); + virtual bool isDataFrameFromAssociatedStation(const Ptr& header) const; + virtual void processUpperFrame(Packet *packet, const Ptr& header); virtual void processLowerFrame(Packet *packet, const Ptr& header); }; diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc index ddb1cf9aa65..0c5a5133e2f 100644 --- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc +++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc @@ -227,8 +227,19 @@ void Dcf::recipientProcessReceivedFrame(Packet *packet, const Ptr(header)) recipientAckProcedure->processReceivedFrame(packet, dataOrMgmtHeader, recipientAckPolicy, this); - if (auto dataHeader = dynamicPtrCast(header)) + if (auto dataHeader = dynamicPtrCast(header)) { + // Keep the immediate ACK independent of Class-3 data admission (IEEE Std 802.11-2024, 10.3.2.11). + if (!mac->isDataFrameFromAssociatedStation(dataHeader)) { + mac->notifyClass3FrameRejected(dataHeader); + EV_INFO << "Discarding data from unassociated transmitter " << dataHeader->getTransmitterAddress() << "\n"; + PacketDropDetails details; + details.setReason(OTHER_PACKET_DROP); + emit(packetDroppedSignal, packet, &details); + delete packet; + return; + } sendUp(recipientDataService->dataFrameReceived(packet, dataHeader)); + } else if (auto mgmtHeader = dynamicPtrCast(header)) sendUp(recipientDataService->managementFrameReceived(packet, mgmtHeader)); else { // TODO else if (auto ctrlFrame = dynamic_cast(frame)) diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc index e64ce4db852..48fabaa1c3d 100644 --- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc +++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc @@ -332,6 +332,16 @@ void Hcf::recipientProcessReceivedFrame(Packet *packet, const Ptr(header)) recipientAckProcedure->processReceivedFrame(packet, dataOrMgmtHeader, check_and_cast(recipientAckPolicy), this); if (auto dataHeader = dynamicPtrCast(header)) { + // Keep the immediate ACK independent of Class-3 data admission (IEEE Std 802.11-2024, 10.3.2.11). + if (!mac->isDataFrameFromAssociatedStation(dataHeader)) { + mac->notifyClass3FrameRejected(dataHeader); + EV_INFO << "Discarding data from unassociated transmitter " << dataHeader->getTransmitterAddress() << "\n"; + PacketDropDetails details; + details.setReason(OTHER_PACKET_DROP); + emit(packetDroppedSignal, packet, &details); + delete packet; + return; + } if (dataHeader->getType() == ST_DATA_WITH_QOS && recipientBlockAckAgreementHandler) recipientBlockAckAgreementHandler->qosFrameReceived(dataHeader, this); sendUp(recipientDataService->dataFrameReceived(packet, dataHeader, recipientBlockAckAgreementHandler)); diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211Class3FrameInd.msg b/src/inet/linklayer/ieee80211/mgmt/Ieee80211Class3FrameInd.msg new file mode 100644 index 00000000000..4d52bb8affa --- /dev/null +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211Class3FrameInd.msg @@ -0,0 +1,16 @@ +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +import inet.common.INETDefs; +import inet.linklayer.common.MacAddress; + +namespace inet::ieee80211; + +// MAC reports a rejected Class-3 MPDU through the management service boundary. +// The original transmitter is captured before any A-MSDU address rewriting. +message Ieee80211Class3FrameInd +{ + MacAddress transmitterAddress; + bool individuallyAddressed; +} diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.cc b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.cc index 1b52306987e..b4a651e9f50 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.cc +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.cc @@ -222,6 +222,20 @@ void Ieee80211MgmtAp::sendBeacon() sendManagementFrame("Beacon", body, ST_BEACON, MacAddress::BROADCAST_ADDRESS); } +void Ieee80211MgmtAp::handleClass3FrameIndication(Ieee80211Class3FrameInd *indication) +{ + // IEEE Std 802.11-2024, 11.3.5.1: authenticated but unassociated + // peers normally receive Disassociation (Table 9-79, reason 7). + auto peer = mib->bssAccessPointData.stations.find(indication->getTransmitterAddress()); + if (indication->getIndividuallyAddressed() && peer != mib->bssAccessPointData.stations.end() && + peer->second == Ieee80211Mib::AUTHENTICATED) { + const auto& body = makeShared(); + body->setReasonCode(RC_NONASS_CLASS3); + sendManagementFrame("Disassoc-Class3", body, ST_DISASSOCIATION, indication->getTransmitterAddress()); + } + delete indication; +} + void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr& header) { const auto& requestBody = packet->peekData(); @@ -567,6 +581,7 @@ void Ieee80211MgmtAp::stop() { cancelEvent(beaconTimer); staList.clear(); + mib->bssAccessPointData.stations.clear(); nextAssociationTransactionId = 0; mib->clearAssociationIds(); Ieee80211MgmtApBase::stop(); diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h index fe8c0c03192..bdfee8239a2 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h @@ -84,6 +84,7 @@ class INET_API Ieee80211MgmtAp : public Ieee80211MgmtApBase /** Implements abstract Ieee80211MgmtBase method */ virtual void handleTimer(cMessage *msg) override; + virtual void handleClass3FrameIndication(Ieee80211Class3FrameInd *indication) override; /** Implements abstract Ieee80211MgmtBase method -- throws an error (no commands supported) */ virtual void handleCommand(int msgkind, cObject *ctrl) override; diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.cc b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.cc index fb0856ec140..22241cc4125 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.cc +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.cc @@ -13,6 +13,13 @@ namespace ieee80211 { Define_Module(Ieee80211MgmtApSimplified); +void Ieee80211MgmtApSimplified::initialize(int stage) +{ + Ieee80211MgmtApBase::initialize(stage); + if (stage == INITSTAGE_LOCAL) + mib->bssAccessPointData.requireAssociatedTransmitter = false; +} + // FIXME add sequence number handling void Ieee80211MgmtApSimplified::handleTimer(cMessage *msg) diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.h b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.h index 13e4e2f2247..59d460dbd34 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.h +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtApSimplified.h @@ -24,6 +24,7 @@ class INET_API Ieee80211MgmtApSimplified : public Ieee80211MgmtApBase { protected: virtual int numInitStages() const override { return NUM_INIT_STAGES; } + virtual void initialize(int stage) override; /** Implements abstract Ieee80211MgmtBase method */ virtual void handleTimer(cMessage *msg) override; diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.cc b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.cc index 3c3bc33903a..2b84b46fc6b 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.cc +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.cc @@ -86,6 +86,11 @@ void Ieee80211MgmtBase::addHtOperation(const Ptr& frame, con setHtOperation(frame, band, mib->getHtOperation()); } +void Ieee80211MgmtBase::handleClass3FrameIndication(Ieee80211Class3FrameInd *indication) +{ + delete indication; +} + void Ieee80211MgmtBase::handleMessageWhenUp(cMessage *msg) { if (msg->isSelfMessage()) { @@ -93,6 +98,8 @@ void Ieee80211MgmtBase::handleMessageWhenUp(cMessage *msg) EV << "Timer expired: " << msg << "\n"; handleTimer(msg); } + else if (msg->arrivedOn("macIn") && dynamic_cast(msg)) + handleClass3FrameIndication(static_cast(msg)); else if (msg->arrivedOn("macIn")) { // process incoming frame EV << "Frame arrived from MAC: " << msg << "\n"; diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.h b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.h index 145da89e902..4a909950d28 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.h +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtBase.h @@ -15,6 +15,7 @@ #include "inet/linklayer/common/MacAddress.h" #include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" #include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h" +#include "inet/linklayer/ieee80211/mgmt/Ieee80211Class3FrameInd_m.h" #include "inet/linklayer/ieee80211/mib/Ieee80211Mib.h" #include "inet/networklayer/contract/IInterfaceTable.h" #include "inet/physicallayer/wireless/ieee80211/mode/Ieee80211Band.h" @@ -57,6 +58,8 @@ class INET_API Ieee80211MgmtBase : public OperationalBase, public cListener /** Should be redefined to handle commands from the "agent" (if present) */ virtual void handleCommand(int msgkind, cObject *ctrl) = 0; + virtual void handleClass3FrameIndication(Ieee80211Class3FrameInd *indication); + /** Utility method for implementing handleUpperMessage(): send message to MAC */ virtual void sendDown(Packet *frame); diff --git a/src/inet/linklayer/ieee80211/mib/Ieee80211Mib.h b/src/inet/linklayer/ieee80211/mib/Ieee80211Mib.h index 691257c2f07..5daa0e02db3 100644 --- a/src/inet/linklayer/ieee80211/mib/Ieee80211Mib.h +++ b/src/inet/linklayer/ieee80211/mib/Ieee80211Mib.h @@ -55,6 +55,8 @@ class INET_API Ieee80211Mib : public SimpleModule class INET_API BssAccessPointData { public: + // Detailed management enforces association; simplified management may model all peers as associated. + bool requireAssociatedTransmitter = true; std::map stations; std::map associationIds; }; diff --git a/tests/fingerprint/showcases.csv b/tests/fingerprint/showcases.csv index f310d0865f0..34f1918d124 100644 --- a/tests/fingerprint/showcases.csv +++ b/tests/fingerprint/showcases.csv @@ -125,7 +125,7 @@ /showcases/visualizer/canvas/ieee80211/, -f omnetpp.ini -c OneNetwork -r 0, 5s, 46cc-9c41/tplx;c427-dece/~tNl;eae3-92e1/~tND;9c21-dd33/tyf, PASS, wireless /showcases/visualizer/canvas/ieee80211/, -f omnetpp.ini -c MultipleNetworks -r 0, 5s, 5094-e358/tplx;3c17-6ee0/~tNl;4dff-fa35/~tND;3be5-9494/tyf, PASS, wireless /showcases/visualizer/canvas/ieee80211/, -f omnetpp.ini -c VisualizingHandover -r 0, 250s, 7fb8-49dc/tplx;2f60-762d/~tNl;0437-f151/~tND;07bb-3973/tyf, PASS, wireless -/showcases/visualizer/canvas/ieee80211/, -f omnetpp.ini -c SignalLevels -r 0, 100s, 51ec-d359/tplx;cec5-d464/~tNl;a0c6-b121/~tND;c099-e5c5/tyf, PASS, Ipv4 +/showcases/visualizer/canvas/ieee80211/, -f omnetpp.ini -c SignalLevels -r 0, 100s, 051b-668f/tplx;cec5-d464/~tNl;a0c6-b121/~tND;c099-e5c5/tyf, PASS, Ipv4 /showcases/visualizer/canvas/submoduleinfo/, -f omnetpp.ini -c PacketCounts -r 0, 5s, 271c-6821/tplx;3c0c-db82/~tNl;8315-9723/~tND;b145-6576/tyf, PASS, wireless Ipv4 # VisualizingSubmoduleInformation extended /showcases/visualizer/canvas/submoduleinfo/, -f omnetpp.ini -c MACStates -r 0, 5s, 271c-6821/tplx;3c0c-db82/~tNl;8315-9723/~tND;4677-fc97/tyf, PASS, wireless Ipv4 diff --git a/tests/module/Ieee80211ApClass3Response_1.test b/tests/module/Ieee80211ApClass3Response_1.test new file mode 100644 index 00000000000..6812c1afdfb --- /dev/null +++ b/tests/module/Ieee80211ApClass3Response_1.test @@ -0,0 +1,252 @@ +%description: +An infrastructure AP must reject an individually addressed ToDS data frame +from a transmitter that is no longer in the AP's associated-station table. +The simplified source STA keeps believing it is associated, so the test +exercises the real wireless transmission and AP DCF receive path rather than +only testing the table lookup helper. + +%file: Ieee80211ApClass3Response.cc + +#include "inet/applications/udpapp/UdpSink.h" +#include "inet/common/InitStages.h" +#include "inet/common/Simsignals.h" +#include "inet/common/packet/Packet.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" +#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame_m.h" +#include "inet/linklayer/ieee80211/mib/Ieee80211Mib.h" + +namespace inet { + +class Ieee80211ApClass3ResponseSink : public UdpSink +{ + public: + int getNumReceived() const { return numReceived; } +}; + +Define_Module(Ieee80211ApClass3ResponseSink); + +class Ieee80211ApClass3ResponseTest : public cSimpleModule, public cListener +{ + protected: + using cListener::finish; + + cMessage *removeSourceTimer = nullptr; + cMessage *checkTimer = nullptr; + cMessage *restoreSourceTimer = nullptr; + cMessage *checkPositiveTimer = nullptr; + ieee80211::Ieee80211Mib *apMib = nullptr; + ieee80211::Ieee80211Mib *sourceMib = nullptr; + Ieee80211ApClass3ResponseSink *sink = nullptr; + cComponent *apDcf = nullptr; + MacAddress sourceAddress; + int sourceAckCount = 0; + int disassociationCount = 0; + + virtual int numInitStages() const override { return NUM_INIT_STAGES; } + + virtual void initialize(int stage) override + { + if (stage == INITSTAGE_LAST) { + apMib = check_and_cast(getModuleByPath("^.ap.wlan[0].mib")); + sourceMib = check_and_cast(getModuleByPath("^.source.wlan[0].mib")); + sink = check_and_cast(getModuleByPath("^.destination.app[0]")); + apDcf = getModuleByPath("^.ap.wlan[0].mac.dcf"); + sourceAddress = sourceMib->address; + ASSERT(!sourceAddress.isUnspecified()); + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + apDcf->subscribe(packetSentToPeerSignal, this); + removeSourceTimer = new cMessage("removeSourceFromAp"); + checkTimer = new cMessage("checkAdmission"); + restoreSourceTimer = new cMessage("restoreSourceToAp"); + checkPositiveTimer = new cMessage("checkAssociatedDelivery"); + scheduleAt(SimTime(500, SIMTIME_US), removeSourceTimer); + scheduleAt(SimTime(5, SIMTIME_MS), checkTimer); + scheduleAt(SimTime(5500, SIMTIME_US), restoreSourceTimer); + scheduleAt(SimTime(9, SIMTIME_MS), checkPositiveTimer); + } + } + + virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *value, cObject *) override + { + if (source == apDcf && signalID == packetSentToPeerSignal && value != nullptr) { + auto packet = check_and_cast(value); + const auto& header = packet->peekAtFront(); + if (header->getType() == ieee80211::ST_ACK && header->getReceiverAddress() == sourceAddress) + sourceAckCount++; + if (header->getType() == ieee80211::ST_DISASSOCIATION) { + ASSERT(header->getReceiverAddress() == sourceAddress); + auto body = packet->peekDataAt(header->getChunkLength()); + ASSERT(body->getReasonCode() == ieee80211::RC_NONASS_CLASS3); + disassociationCount++; + } + } + } + + virtual void handleMessage(cMessage *message) override + { + if (message == removeSourceTimer) { + delete removeSourceTimer; + removeSourceTimer = nullptr; + auto it = apMib->bssAccessPointData.stations.find(sourceAddress); + ASSERT(it != apMib->bssAccessPointData.stations.end()); + ASSERT(it->second == ieee80211::Ieee80211Mib::ASSOCIATED); + it->second = ieee80211::Ieee80211Mib::AUTHENTICATED; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::AUTHENTICATED); + std::cout << "Left source authenticated but unassociated at AP.\n"; + } + else if (message == checkTimer) { + delete checkTimer; + checkTimer = nullptr; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::AUTHENTICATED); + ASSERT(sourceAckCount > 0); + ASSERT(disassociationCount == 1); + ASSERT(sink->getNumReceived() == 0); + std::cout << "AP acknowledged and rejected Class3 data, then sent Disassociation reason7.\n"; + } + else if (message == restoreSourceTimer) { + delete restoreSourceTimer; + restoreSourceTimer = nullptr; + apMib->bssAccessPointData.stations[sourceAddress] = ieee80211::Ieee80211Mib::ASSOCIATED; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + std::cout << "Restored source to AP association table while source remained associated locally.\n"; + } + else if (message == checkPositiveTimer) { + delete checkPositiveTimer; + checkPositiveTimer = nullptr; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + ASSERT(sink->getNumReceived() == 1); + std::cout << "AP forwarded the subsequent data frame after source re-association.\n"; + } + else + throw cRuntimeError("Unexpected timer"); + } + + virtual void finish() override + { + if (apDcf != nullptr) + apDcf->unsubscribe(packetSentToPeerSignal, this); + if (removeSourceTimer != nullptr) + cancelAndDelete(removeSourceTimer); + if (checkTimer != nullptr) + cancelAndDelete(checkTimer); + if (restoreSourceTimer != nullptr) + cancelAndDelete(restoreSourceTimer); + if (checkPositiveTimer != nullptr) + cancelAndDelete(checkPositiveTimer); + } +}; + +Define_Module(Ieee80211ApClass3ResponseTest); + +} // namespace inet + +%file: test.ned + +import inet.applications.udpapp.UdpSink; +import inet.common.SimpleModule; +import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; +import inet.node.inet.WirelessHost; +import inet.node.wireless.AccessPoint; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; + +simple Ieee80211ApClass3ResponseSink extends UdpSink +{ + parameters: + @class(::inet::Ieee80211ApClass3ResponseSink); +} + +simple Ieee80211ApClass3ResponseTest extends SimpleModule +{ + parameters: + @class(::inet::Ieee80211ApClass3ResponseTest); +} + +network Ieee80211ApClass3ResponseTestNetwork +{ + submodules: + radioMedium: Ieee80211ScalarRadioMedium; + configurator: Ipv4NetworkConfigurator; + ap: AccessPoint; + source: WirelessHost { + parameters: + wlan[*].mgmt.typename = "Ieee80211MgmtStaSimplified"; + wlan[*].agent.typename = ""; + } + destination: WirelessHost { + parameters: + wlan[*].mgmt.typename = "Ieee80211MgmtStaSimplified"; + wlan[*].agent.typename = ""; + } + test: Ieee80211ApClass3ResponseTest; +} + +%inifile: omnetpp.ini + +[General] +network = Ieee80211ApClass3ResponseTestNetwork +ned-path = .;../../../../src;../../lib +seed-set = 0 +sim-time-limit = 10ms +cmdenv-express-mode = false +record-vector-results = false +record-scalar-results = false + +*.configurator.addStaticRoutes = true +**.arp.typename = "GlobalArp" + +*.ap.wlan[0].address = "02:00:00:00:00:01" +*.source.wlan[0].address = "02:00:00:00:00:02" +*.destination.wlan[0].address = "02:00:00:00:00:03" +*.ap.wlan[0].mgmt.typename = "Ieee80211MgmtAp" +*.ap.wlan[0].mgmt.ssid = "audit-bss" +*.source.wlan[0].mgmt.accessPointAddress = "02:00:00:00:00:01" +*.destination.wlan[0].mgmt.accessPointAddress = "02:00:00:00:00:01" + +**.wlan[*].opMode = "g(erp)" +**.wlan[*].bitrate = 24Mbps +**.wlan[*].radio.transmitter.power = 50mW +**.wlan[*].radio.receiver.sensitivity = -85dBm +**.wlan[*].radio.receiver.snirThreshold = 4dB + +*.ap.mobility.typename = "StationaryMobility" +*.source.mobility.typename = "StationaryMobility" +*.destination.mobility.typename = "StationaryMobility" +**.mobility.initFromDisplayString = false +*.ap.mobility.initialX = 5m +*.ap.mobility.initialY = 0m +*.ap.mobility.initialZ = 0m +*.source.mobility.initialX = 0m +*.source.mobility.initialY = 0m +*.source.mobility.initialZ = 0m +*.destination.mobility.initialX = 10m +*.destination.mobility.initialY = 0m +*.destination.mobility.initialZ = 0m + +*.source.numApps = 1 +*.source.app[0].typename = "UdpBasicApp" +*.source.app[0].destAddresses = "destination" +*.source.app[0].destPort = 5000 +*.source.app[0].messageLength = 100B +*.source.app[0].startTime = 2ms +*.source.app[0].stopTime = 8ms +*.source.app[0].sendInterval = 5ms +*.destination.numApps = 1 +*.destination.app[0].typename = "Ieee80211ApClass3ResponseSink" +*.destination.app[0].localPort = 5000 + +%contains: stdout +Left source authenticated but unassociated at AP. + +%contains: stdout +AP acknowledged and rejected Class3 data, then sent Disassociation reason7. + +%contains: stdout +Restored source to AP association table while source remained associated locally. + +%contains: stdout +AP forwarded the subsequent data frame after source re-association. diff --git a/tests/module/Ieee80211ApSourceAdmissionQos_1.test b/tests/module/Ieee80211ApSourceAdmissionQos_1.test new file mode 100644 index 00000000000..190026216a9 --- /dev/null +++ b/tests/module/Ieee80211ApSourceAdmissionQos_1.test @@ -0,0 +1,244 @@ +%description: +An infrastructure AP must reject an individually addressed ToDS data frame +from a transmitter that is no longer in the AP's associated-station table. +The simplified source STA keeps believing it is associated, so the test +exercises the real wireless transmission and AP DCF receive path rather than +only testing the table lookup helper. + +%file: Ieee80211ApSourceAdmissionQos.cc + +#include "inet/applications/udpapp/UdpSink.h" +#include "inet/common/InitStages.h" +#include "inet/common/Simsignals.h" +#include "inet/common/packet/Packet.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" +#include "inet/linklayer/ieee80211/mib/Ieee80211Mib.h" + +namespace inet { + +class Ieee80211ApSourceAdmissionQosSink : public UdpSink +{ + public: + int getNumReceived() const { return numReceived; } +}; + +Define_Module(Ieee80211ApSourceAdmissionQosSink); + +class Ieee80211ApSourceAdmissionQosTest : public cSimpleModule, public cListener +{ + protected: + using cListener::finish; + + cMessage *removeSourceTimer = nullptr; + cMessage *checkTimer = nullptr; + cMessage *restoreSourceTimer = nullptr; + cMessage *checkPositiveTimer = nullptr; + ieee80211::Ieee80211Mib *apMib = nullptr; + ieee80211::Ieee80211Mib *sourceMib = nullptr; + Ieee80211ApSourceAdmissionQosSink *sink = nullptr; + cComponent *apHcf = nullptr; + MacAddress sourceAddress; + int sourceAckCount = 0; + + virtual int numInitStages() const override { return NUM_INIT_STAGES; } + + virtual void initialize(int stage) override + { + if (stage == INITSTAGE_LAST) { + apMib = check_and_cast(getModuleByPath("^.ap.wlan[0].mib")); + sourceMib = check_and_cast(getModuleByPath("^.source.wlan[0].mib")); + sink = check_and_cast(getModuleByPath("^.destination.app[0]")); + apHcf = getModuleByPath("^.ap.wlan[0].mac.hcf"); + sourceAddress = sourceMib->address; + ASSERT(!sourceAddress.isUnspecified()); + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + apHcf->subscribe(packetSentToPeerSignal, this); + removeSourceTimer = new cMessage("removeSourceFromAp"); + checkTimer = new cMessage("checkAdmission"); + restoreSourceTimer = new cMessage("restoreSourceToAp"); + checkPositiveTimer = new cMessage("checkAssociatedDelivery"); + scheduleAt(SimTime(500, SIMTIME_US), removeSourceTimer); + scheduleAt(SimTime(5, SIMTIME_MS), checkTimer); + scheduleAt(SimTime(5500, SIMTIME_US), restoreSourceTimer); + scheduleAt(SimTime(9, SIMTIME_MS), checkPositiveTimer); + } + } + + virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *value, cObject *) override + { + if (source == apHcf && signalID == packetSentToPeerSignal && value != nullptr) { + auto packet = check_and_cast(value); + const auto& header = packet->peekAtFront(); + if (header->getType() == ieee80211::ST_ACK && header->getReceiverAddress() == sourceAddress) + sourceAckCount++; + } + } + + virtual void handleMessage(cMessage *message) override + { + if (message == removeSourceTimer) { + delete removeSourceTimer; + removeSourceTimer = nullptr; + auto it = apMib->bssAccessPointData.stations.find(sourceAddress); + ASSERT(it != apMib->bssAccessPointData.stations.end()); + ASSERT(it->second == ieee80211::Ieee80211Mib::ASSOCIATED); + apMib->bssAccessPointData.stations.erase(it); + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.find(sourceAddress) == apMib->bssAccessPointData.stations.end()); + std::cout << "Removed source from AP association table while source remained associated locally.\n"; + } + else if (message == checkTimer) { + delete checkTimer; + checkTimer = nullptr; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.find(sourceAddress) == apMib->bssAccessPointData.stations.end()); + ASSERT(sourceAckCount > 0); + ASSERT(sink->getNumReceived() == 0); + std::cout << "AP rejected individually addressed ToDS data from an unassociated source STA.\n"; + } + else if (message == restoreSourceTimer) { + delete restoreSourceTimer; + restoreSourceTimer = nullptr; + apMib->bssAccessPointData.stations[sourceAddress] = ieee80211::Ieee80211Mib::ASSOCIATED; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + std::cout << "Restored source to AP association table while source remained associated locally.\n"; + } + else if (message == checkPositiveTimer) { + delete checkPositiveTimer; + checkPositiveTimer = nullptr; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + ASSERT(sink->getNumReceived() == 1); + std::cout << "AP forwarded the subsequent data frame after source re-association.\n"; + } + else + throw cRuntimeError("Unexpected timer"); + } + + virtual void finish() override + { + if (apHcf != nullptr) + apHcf->unsubscribe(packetSentToPeerSignal, this); + if (removeSourceTimer != nullptr) + cancelAndDelete(removeSourceTimer); + if (checkTimer != nullptr) + cancelAndDelete(checkTimer); + if (restoreSourceTimer != nullptr) + cancelAndDelete(restoreSourceTimer); + if (checkPositiveTimer != nullptr) + cancelAndDelete(checkPositiveTimer); + } +}; + +Define_Module(Ieee80211ApSourceAdmissionQosTest); + +} // namespace inet + +%file: test.ned + +import inet.applications.udpapp.UdpSink; +import inet.common.SimpleModule; +import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; +import inet.node.inet.WirelessHost; +import inet.node.wireless.AccessPoint; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; + +simple Ieee80211ApSourceAdmissionQosSink extends UdpSink +{ + parameters: + @class(::inet::Ieee80211ApSourceAdmissionQosSink); +} + +simple Ieee80211ApSourceAdmissionQosTest extends SimpleModule +{ + parameters: + @class(::inet::Ieee80211ApSourceAdmissionQosTest); +} + +network Ieee80211ApSourceAdmissionQosTestNetwork +{ + submodules: + radioMedium: Ieee80211ScalarRadioMedium; + configurator: Ipv4NetworkConfigurator; + ap: AccessPoint; + source: WirelessHost { + parameters: + wlan[*].mgmt.typename = "Ieee80211MgmtStaSimplified"; + wlan[*].agent.typename = ""; + } + destination: WirelessHost { + parameters: + wlan[*].mgmt.typename = "Ieee80211MgmtStaSimplified"; + wlan[*].agent.typename = ""; + } + test: Ieee80211ApSourceAdmissionQosTest; +} + +%inifile: omnetpp.ini + +[General] +network = Ieee80211ApSourceAdmissionQosTestNetwork +ned-path = .;../../../../src;../../lib +seed-set = 0 +sim-time-limit = 10ms +cmdenv-express-mode = false +record-vector-results = false +record-scalar-results = false + +*.configurator.addStaticRoutes = true +**.arp.typename = "GlobalArp" + +*.ap.wlan[0].address = "02:00:00:00:00:01" +*.source.wlan[0].address = "02:00:00:00:00:02" +*.destination.wlan[0].address = "02:00:00:00:00:03" +*.ap.wlan[0].mgmt.typename = "Ieee80211MgmtAp" +*.ap.wlan[0].mgmt.ssid = "audit-bss" +*.source.wlan[0].mgmt.accessPointAddress = "02:00:00:00:00:01" +*.destination.wlan[0].mgmt.accessPointAddress = "02:00:00:00:00:01" + +**.wlan[*].opMode = "g(erp)" +**.wlan[*].mac.qosStation = true +**.wlan[*].bitrate = 24Mbps +**.wlan[*].radio.transmitter.power = 50mW +**.wlan[*].radio.receiver.sensitivity = -85dBm +**.wlan[*].radio.receiver.snirThreshold = 4dB + +*.ap.mobility.typename = "StationaryMobility" +*.source.mobility.typename = "StationaryMobility" +*.destination.mobility.typename = "StationaryMobility" +**.mobility.initFromDisplayString = false +*.ap.mobility.initialX = 5m +*.ap.mobility.initialY = 0m +*.ap.mobility.initialZ = 0m +*.source.mobility.initialX = 0m +*.source.mobility.initialY = 0m +*.source.mobility.initialZ = 0m +*.destination.mobility.initialX = 10m +*.destination.mobility.initialY = 0m +*.destination.mobility.initialZ = 0m + +*.source.numApps = 1 +*.source.app[0].typename = "UdpBasicApp" +*.source.app[0].destAddresses = "destination" +*.source.app[0].destPort = 5000 +*.source.app[0].messageLength = 100B +*.source.app[0].startTime = 2ms +*.source.app[0].stopTime = 8ms +*.source.app[0].sendInterval = 5ms +*.destination.numApps = 1 +*.destination.app[0].typename = "Ieee80211ApSourceAdmissionQosSink" +*.destination.app[0].localPort = 5000 + +%contains: stdout +Removed source from AP association table while source remained associated locally. + +%contains: stdout +AP rejected individually addressed ToDS data from an unassociated source STA. + +%contains: stdout +Restored source to AP association table while source remained associated locally. + +%contains: stdout +AP forwarded the subsequent data frame after source re-association. diff --git a/tests/module/Ieee80211ApSourceAdmission_1.test b/tests/module/Ieee80211ApSourceAdmission_1.test new file mode 100644 index 00000000000..37ad931ea2f --- /dev/null +++ b/tests/module/Ieee80211ApSourceAdmission_1.test @@ -0,0 +1,243 @@ +%description: +An infrastructure AP must reject an individually addressed ToDS data frame +from a transmitter that is no longer in the AP's associated-station table. +The simplified source STA keeps believing it is associated, so the test +exercises the real wireless transmission and AP DCF receive path rather than +only testing the table lookup helper. + +%file: Ieee80211ApSourceAdmission.cc + +#include "inet/applications/udpapp/UdpSink.h" +#include "inet/common/InitStages.h" +#include "inet/common/Simsignals.h" +#include "inet/common/packet/Packet.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" +#include "inet/linklayer/ieee80211/mib/Ieee80211Mib.h" + +namespace inet { + +class Ieee80211ApSourceAdmissionSink : public UdpSink +{ + public: + int getNumReceived() const { return numReceived; } +}; + +Define_Module(Ieee80211ApSourceAdmissionSink); + +class Ieee80211ApSourceAdmissionTest : public cSimpleModule, public cListener +{ + protected: + using cListener::finish; + + cMessage *removeSourceTimer = nullptr; + cMessage *checkTimer = nullptr; + cMessage *restoreSourceTimer = nullptr; + cMessage *checkPositiveTimer = nullptr; + ieee80211::Ieee80211Mib *apMib = nullptr; + ieee80211::Ieee80211Mib *sourceMib = nullptr; + Ieee80211ApSourceAdmissionSink *sink = nullptr; + cComponent *apDcf = nullptr; + MacAddress sourceAddress; + int sourceAckCount = 0; + + virtual int numInitStages() const override { return NUM_INIT_STAGES; } + + virtual void initialize(int stage) override + { + if (stage == INITSTAGE_LAST) { + apMib = check_and_cast(getModuleByPath("^.ap.wlan[0].mib")); + sourceMib = check_and_cast(getModuleByPath("^.source.wlan[0].mib")); + sink = check_and_cast(getModuleByPath("^.destination.app[0]")); + apDcf = getModuleByPath("^.ap.wlan[0].mac.dcf"); + sourceAddress = sourceMib->address; + ASSERT(!sourceAddress.isUnspecified()); + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + apDcf->subscribe(packetSentToPeerSignal, this); + removeSourceTimer = new cMessage("removeSourceFromAp"); + checkTimer = new cMessage("checkAdmission"); + restoreSourceTimer = new cMessage("restoreSourceToAp"); + checkPositiveTimer = new cMessage("checkAssociatedDelivery"); + scheduleAt(SimTime(500, SIMTIME_US), removeSourceTimer); + scheduleAt(SimTime(5, SIMTIME_MS), checkTimer); + scheduleAt(SimTime(5500, SIMTIME_US), restoreSourceTimer); + scheduleAt(SimTime(9, SIMTIME_MS), checkPositiveTimer); + } + } + + virtual void receiveSignal(cComponent *source, simsignal_t signalID, cObject *value, cObject *) override + { + if (source == apDcf && signalID == packetSentToPeerSignal && value != nullptr) { + auto packet = check_and_cast(value); + const auto& header = packet->peekAtFront(); + if (header->getType() == ieee80211::ST_ACK && header->getReceiverAddress() == sourceAddress) + sourceAckCount++; + } + } + + virtual void handleMessage(cMessage *message) override + { + if (message == removeSourceTimer) { + delete removeSourceTimer; + removeSourceTimer = nullptr; + auto it = apMib->bssAccessPointData.stations.find(sourceAddress); + ASSERT(it != apMib->bssAccessPointData.stations.end()); + ASSERT(it->second == ieee80211::Ieee80211Mib::ASSOCIATED); + apMib->bssAccessPointData.stations.erase(it); + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.find(sourceAddress) == apMib->bssAccessPointData.stations.end()); + std::cout << "Removed source from AP association table while source remained associated locally.\n"; + } + else if (message == checkTimer) { + delete checkTimer; + checkTimer = nullptr; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.find(sourceAddress) == apMib->bssAccessPointData.stations.end()); + ASSERT(sourceAckCount > 0); + ASSERT(sink->getNumReceived() == 0); + std::cout << "AP rejected individually addressed ToDS data from an unassociated source STA.\n"; + } + else if (message == restoreSourceTimer) { + delete restoreSourceTimer; + restoreSourceTimer = nullptr; + apMib->bssAccessPointData.stations[sourceAddress] = ieee80211::Ieee80211Mib::ASSOCIATED; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + std::cout << "Restored source to AP association table while source remained associated locally.\n"; + } + else if (message == checkPositiveTimer) { + delete checkPositiveTimer; + checkPositiveTimer = nullptr; + ASSERT(sourceMib->bssStationData.isAssociated); + ASSERT(apMib->bssAccessPointData.stations.at(sourceAddress) == ieee80211::Ieee80211Mib::ASSOCIATED); + ASSERT(sink->getNumReceived() == 1); + std::cout << "AP forwarded the subsequent data frame after source re-association.\n"; + } + else + throw cRuntimeError("Unexpected timer"); + } + + virtual void finish() override + { + if (apDcf != nullptr) + apDcf->unsubscribe(packetSentToPeerSignal, this); + if (removeSourceTimer != nullptr) + cancelAndDelete(removeSourceTimer); + if (checkTimer != nullptr) + cancelAndDelete(checkTimer); + if (restoreSourceTimer != nullptr) + cancelAndDelete(restoreSourceTimer); + if (checkPositiveTimer != nullptr) + cancelAndDelete(checkPositiveTimer); + } +}; + +Define_Module(Ieee80211ApSourceAdmissionTest); + +} // namespace inet + +%file: test.ned + +import inet.applications.udpapp.UdpSink; +import inet.common.SimpleModule; +import inet.networklayer.configurator.ipv4.Ipv4NetworkConfigurator; +import inet.node.inet.WirelessHost; +import inet.node.wireless.AccessPoint; +import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium; + +simple Ieee80211ApSourceAdmissionSink extends UdpSink +{ + parameters: + @class(::inet::Ieee80211ApSourceAdmissionSink); +} + +simple Ieee80211ApSourceAdmissionTest extends SimpleModule +{ + parameters: + @class(::inet::Ieee80211ApSourceAdmissionTest); +} + +network Ieee80211ApSourceAdmissionTestNetwork +{ + submodules: + radioMedium: Ieee80211ScalarRadioMedium; + configurator: Ipv4NetworkConfigurator; + ap: AccessPoint; + source: WirelessHost { + parameters: + wlan[*].mgmt.typename = "Ieee80211MgmtStaSimplified"; + wlan[*].agent.typename = ""; + } + destination: WirelessHost { + parameters: + wlan[*].mgmt.typename = "Ieee80211MgmtStaSimplified"; + wlan[*].agent.typename = ""; + } + test: Ieee80211ApSourceAdmissionTest; +} + +%inifile: omnetpp.ini + +[General] +network = Ieee80211ApSourceAdmissionTestNetwork +ned-path = .;../../../../src;../../lib +seed-set = 0 +sim-time-limit = 10ms +cmdenv-express-mode = false +record-vector-results = false +record-scalar-results = false + +*.configurator.addStaticRoutes = true +**.arp.typename = "GlobalArp" + +*.ap.wlan[0].address = "02:00:00:00:00:01" +*.source.wlan[0].address = "02:00:00:00:00:02" +*.destination.wlan[0].address = "02:00:00:00:00:03" +*.ap.wlan[0].mgmt.typename = "Ieee80211MgmtAp" +*.ap.wlan[0].mgmt.ssid = "audit-bss" +*.source.wlan[0].mgmt.accessPointAddress = "02:00:00:00:00:01" +*.destination.wlan[0].mgmt.accessPointAddress = "02:00:00:00:00:01" + +**.wlan[*].opMode = "g(erp)" +**.wlan[*].bitrate = 24Mbps +**.wlan[*].radio.transmitter.power = 50mW +**.wlan[*].radio.receiver.sensitivity = -85dBm +**.wlan[*].radio.receiver.snirThreshold = 4dB + +*.ap.mobility.typename = "StationaryMobility" +*.source.mobility.typename = "StationaryMobility" +*.destination.mobility.typename = "StationaryMobility" +**.mobility.initFromDisplayString = false +*.ap.mobility.initialX = 5m +*.ap.mobility.initialY = 0m +*.ap.mobility.initialZ = 0m +*.source.mobility.initialX = 0m +*.source.mobility.initialY = 0m +*.source.mobility.initialZ = 0m +*.destination.mobility.initialX = 10m +*.destination.mobility.initialY = 0m +*.destination.mobility.initialZ = 0m + +*.source.numApps = 1 +*.source.app[0].typename = "UdpBasicApp" +*.source.app[0].destAddresses = "destination" +*.source.app[0].destPort = 5000 +*.source.app[0].messageLength = 100B +*.source.app[0].startTime = 2ms +*.source.app[0].stopTime = 8ms +*.source.app[0].sendInterval = 5ms +*.destination.numApps = 1 +*.destination.app[0].typename = "Ieee80211ApSourceAdmissionSink" +*.destination.app[0].localPort = 5000 + +%contains: stdout +Removed source from AP association table while source remained associated locally. + +%contains: stdout +AP rejected individually addressed ToDS data from an unassociated source STA. + +%contains: stdout +Restored source to AP association table while source remained associated locally. + +%contains: stdout +AP forwarded the subsequent data frame after source re-association.