Skip to content
Draft
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
2 changes: 1 addition & 1 deletion examples/wireless/handover/omnetpp.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ network = HandoverNetwork
**.ap1.wlan[*].mgmt.ssid = "AP1"
**.ap2.wlan[*].mgmt.ssid = "AP2"
**.ap*.wlan[*].mgmt.beaconInterval = 100ms
**.wlan[*].mgmt.numAuthSteps = 4
**.wlan[*].mgmt.numAuthSteps = 2

# mobility
**.mobility.initFromDisplayString = true
Expand Down
16 changes: 16 additions & 0 deletions src/inet/linklayer/ieee80211/mac/Ieee80211BssidReq.msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

import inet.common.INETDefs;
import inet.common.TagBase;
import inet.linklayer.common.MacAddress;

namespace inet;

// The management procedure's BSSID for Address3 of an outgoing management frame.
// It is independent of the receiver address and survives queuing/state changes.
class Ieee80211BssidReq extends TagBase
{
MacAddress bssid;
}
6 changes: 5 additions & 1 deletion src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


#include "inet/linklayer/ieee80211/mac/Ieee80211Mac.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211BssidReq_m.h"

#include <algorithm>

Expand Down Expand Up @@ -164,7 +165,10 @@ void Ieee80211Mac::handleMgmtPacket(Packet *packet)
const auto& header = makeShared<Ieee80211MgmtHeader>();
header->setType((Ieee80211FrameType)packet->getTag<Ieee80211SubtypeReq>()->getSubtype());
header->setReceiverAddress(packet->getTag<MacAddressReq>()->getDestAddress());
if (mib->mode == Ieee80211Mib::INFRASTRUCTURE && mib->bssStationData.stationType == Ieee80211Mib::ACCESS_POINT)
// IEEE Std 802.11-2024, 9.3.3.1: management supplies its intended BSSID.
if (auto bssid = packet->findTag<Ieee80211BssidReq>())
header->setAddress3(bssid->getBssid());
else if (mib->mode == Ieee80211Mib::INFRASTRUCTURE && mib->bssStationData.stationType == Ieee80211Mib::ACCESS_POINT)
header->setAddress3(mib->bssData.bssid);
packet->insertAtFront(header);
packet->insertAtBack(makeShared<Ieee80211MacTrailer>());
Expand Down
61 changes: 19 additions & 42 deletions src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ void Ieee80211MgmtAp::initialize(int stage)
ssid = par("ssid").stdstringValue();
beaconInterval = normalizeIeee80211BeaconInterval(par("beaconInterval"));
numAuthSteps = par("numAuthSteps");
if (numAuthSteps != 2 && numAuthSteps != 4)
throw cRuntimeError("parameter 'numAuthSteps' (number of frames exchanged during authentication) must be 2 or 4, not %d", numAuthSteps);
if (numAuthSteps != 2)
throw cRuntimeError("Open System authentication requires numAuthSteps=2; unsupported value %d", numAuthSteps);
WATCH(ssid);
WATCH(beaconInterval);
WATCH(numAuthSteps);
Expand Down Expand Up @@ -226,7 +226,17 @@ void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr<const
{
const auto& requestBody = packet->peekData<Ieee80211AuthenticationFrame>();
int frameAuthSeq = requestBody->getSequenceNumber();
EV << "Processing Authentication frame, seqNum=" << frameAuthSeq << "\n";
// IEEE Std 802.11-2024, 12.3.3.2: Open System is exactly two messages.
// Reject unsupported inputs before creating or downgrading peer state.
if (requestBody->getAlgorithmNumber() != 0 || frameAuthSeq != 1) {
const auto& body = makeShared<Ieee80211AuthenticationFrame>();
body->setAlgorithmNumber(requestBody->getAlgorithmNumber());
body->setSequenceNumber(2);
body->setStatusCode(requestBody->getAlgorithmNumber() != 0 ? SC_AUTH_ALG0_UNSUP : SC_AUTH_OUT_OF_SEQ);
sendManagementFrame("Auth-ERROR", body, ST_AUTHENTICATION, header->getTransmitterAddress());
delete packet;
return;
}

// create STA entry if needed
StaInfo *sta = lookupSenderSTA(header);
Expand Down Expand Up @@ -260,48 +270,15 @@ void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr<const
sendDisAssocNotification(sta->address);
}

// check authentication sequence number is OK
if (frameAuthSeq != sta->authSeqExpected) {
// wrong sequence number: send error and return
EV << "Wrong sequence number, " << sta->authSeqExpected << " expected\n";
const auto& body = makeShared<Ieee80211AuthenticationFrame>();
body->setStatusCode(SC_AUTH_OUT_OF_SEQ);
sendManagementFrame("Auth-ERROR", body, ST_AUTHENTICATION, header->getTransmitterAddress());
delete packet;
sta->authSeqExpected = 1; // go back to start square
return;
}

// station is authenticated if it made it through the required number of steps
bool isLast = (frameAuthSeq + 1 == numAuthSteps);

// send OK response (we don't model the cryptography part, just assume
// successful authentication every time)
EV << "Sending Authentication frame, seqNum=" << (frameAuthSeq + 1) << "\n";
const auto& body = makeShared<Ieee80211AuthenticationFrame>();
body->setSequenceNumber(frameAuthSeq + 1);
body->setAlgorithmNumber(0);
body->setSequenceNumber(2);
body->setStatusCode(SC_SUCCESSFUL);
body->setIsLast(isLast);
// TODO frame length could be increased to account for challenge text length etc.
sendManagementFrame(isLast ? "Auth-OK" : "Auth", body, ST_AUTHENTICATION, header->getTransmitterAddress());

sendManagementFrame("Auth-OK", body, ST_AUTHENTICATION, header->getTransmitterAddress());
delete packet;

// update status
if (isLast) {
bool wasAssociated = mib->bssAccessPointData.stations[sta->address] == Ieee80211Mib::ASSOCIATED;
if (wasAssociated)
mib->releaseAssociationId(sta->address);
mib->bssAccessPointData.stations[sta->address] = Ieee80211Mib::AUTHENTICATED; // TODO only when ACK of this frame arrives
mib->removePeerHtCapabilities(sta->address);
if (wasAssociated)
sendDisAssocNotification(sta->address);
EV << "STA authenticated\n";
}
else {
sta->authSeqExpected += 2;
EV << "Expecting Authentication frame " << sta->authSeqExpected << "\n";
}
mib->bssAccessPointData.stations[sta->address] = Ieee80211Mib::AUTHENTICATED; // TODO commit when response ACK arrives
mib->removePeerHtCapabilities(sta->address);
sta->authSeqExpected = 1;
}

void Ieee80211MgmtAp::handleDeauthenticationFrame(Packet *packet, const Ptr<const Ieee80211MgmtHeader>& header)
Expand Down
2 changes: 1 addition & 1 deletion src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.ned
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ simple Ieee80211MgmtAp extends SimpleModule like IIeee80211Mgmt
@class(Ieee80211MgmtAp);
string ssid = default("SSID");
double beaconInterval @unit(s) = default(100ms); // 1..65535 TUs; rounded down to whole 1024 us TUs for scheduling and advertisement
int numAuthSteps = default(4); // Use 2 for Open System auth, 4 for WEP
int numAuthSteps = default(2); // Open System authentication; only 2 is supported
string interfaceTableModule;
string radioModule = default("^.radio"); // The path to the Radio module //FIXME remove default value
string mibModule;
Expand Down
10 changes: 3 additions & 7 deletions src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrame.msg
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,15 @@ class Ieee80211MgmtFrame extends FieldsChunk
//
// Authentication frame body format.
//
// Note: the "isLast" field is not part of the standard.
// The authentication procedure is simulated by this model by exchanging
// a number of "dummy" authentication frames without real contents;
// the "isLast" field gets set by the AP to signal the STA that
// the authentication procedure has completed and it does not need to
// send any more Authentication frames.
// Open System uses wire transaction numbers 1 and 2 (IEEE 802.11-2024, 12.3.3.2).
//
class Ieee80211AuthenticationFrame extends Ieee80211MgmtFrame
{
chunkLength = B(6); // add 2+len bytes for challenge text if present
uint16_t algorithmNumber = 0; // Open System; preserve unsupported wire values for rejection
unsigned short sequenceNumber;
Ieee80211StatusCode statusCode;
bool isLast; // not part of the standard; see above
bool isLast; // deprecated compatibility field, not serialized or used by management
}

//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ void Ieee80211MgmtFrameSerializer::serializeFields(MemoryOutputStream& stream, c
if (auto authenticationFrame = dynamicPtrCast<const Ieee80211AuthenticationFrame>(chunk)) {
// type = ST_AUTHENTICATION;
// 1 Authentication algorithm number
stream.writeUint16Le(0);
stream.writeUint16Le(authenticationFrame->getAlgorithmNumber());
// 2 Authentication transaction sequence number
stream.writeUint16Le(authenticationFrame->getSequenceNumber());
// 3 Status code The status code information is reserved in certain Authentication frames as defined in Table 7-17.
Expand Down Expand Up @@ -838,7 +838,7 @@ const Ptr<Chunk> Ieee80211MgmtFrameSerializer::deserializeFields(MemoryInputStre
{
if (typeInfo == typeid(Ieee80211AuthenticationFrame)) {
auto frame = makeShared<Ieee80211AuthenticationFrame>();
stream.readUint16Le();
frame->setAlgorithmNumber(stream.readUint16Le());
frame->setSequenceNumber(stream.readUint16Le());
frame->setStatusCode((Ieee80211StatusCode)stream.readUint16Le());
readHtElements(stream, frame, HT_ELEMENT_NONE, 0);
Expand Down
51 changes: 10 additions & 41 deletions src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "inet/common/packet/Message.h"
#include "inet/linklayer/common/MacAddressTag_m.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211SubtypeTag_m.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211BssidReq_m.h"
#include "inet/networklayer/common/NetworkInterface.h"
#include "inet/physicallayer/wireless/common/contract/packetlevel/IRadioMedium.h"
#include "inet/physicallayer/wireless/common/contract/packetlevel/RadioControlInfo_m.h"
Expand Down Expand Up @@ -319,6 +320,8 @@ void Ieee80211MgmtSta::sendManagementFrame(const char *name, const Ptr<Ieee80211
{
auto packet = new Packet(name);
packet->addTag<MacAddressReq>()->setDestAddress(address);
// IEEE Std 802.11-2024, 9.3.3.1: use the target AP, including before association.
packet->addTag<Ieee80211BssidReq>()->setBssid(address);
packet->addTag<Ieee80211SubtypeReq>()->setSubtype(subtype);
packet->insertAtBack(body);
sendDown(packet);
Expand Down Expand Up @@ -738,48 +741,14 @@ void Ieee80211MgmtSta::handleAuthenticationFrame(Packet *packet, const Ptr<const
return;
}

// check authentication sequence number is OK
if (frameAuthSeq != ap->authSeqExpected) {
// wrong sequence number: send error and return
EV << "Wrong sequence number, " << ap->authSeqExpected << " expected\n";
const auto& body = makeShared<Ieee80211AuthenticationFrame>();
body->setStatusCode(SC_AUTH_OUT_OF_SEQ);
sendManagementFrame("Auth-ERROR", body, ST_AUTHENTICATION, header->getTransmitterAddress());
delete packet;

// cancel timeout, send error to agent
cancelAndDelete(ap->authTimeoutMsg);
ap->authTimeoutMsg = nullptr;
sendAuthenticationConfirm(ap, PRC_REFUSED); // TODO or what resultCode?
return;
}

// check if more exchanges are needed for auth to be complete
// IEEE Std 802.11-2024, 12.3.3.2: wire algorithm/transaction fields
// determine completion, never the old model-only isLast flag.
bool validResponse = requestBody->getAlgorithmNumber() == 0 && frameAuthSeq == 2;
int statusCode = requestBody->getStatusCode();

if (statusCode == SC_SUCCESSFUL && !requestBody->isLast()) {
EV << "More steps required, sending another Authentication frame\n";

// more steps required, send another Authentication frame
const auto& body = makeShared<Ieee80211AuthenticationFrame>();
body->setSequenceNumber(frameAuthSeq + 1);
body->setStatusCode(SC_SUCCESSFUL);
// TODO frame length could be increased to account for challenge text length etc.
sendManagementFrame("Auth", body, ST_AUTHENTICATION, address);
ap->authSeqExpected += 2;
}
else {
if (statusCode == SC_SUCCESSFUL)
EV << "Authentication successful\n";
else
EV << "Authentication failed\n";

// authentication completed
ap->isAuthenticated = (statusCode == SC_SUCCESSFUL);
cancelAndDelete(ap->authTimeoutMsg);
ap->authTimeoutMsg = nullptr;
sendAuthenticationConfirm(ap, statusCodeToPrimResultCode(statusCode));
}
ap->isAuthenticated = validResponse && statusCode == SC_SUCCESSFUL;
cancelAndDelete(ap->authTimeoutMsg);
ap->authTimeoutMsg = nullptr;
sendAuthenticationConfirm(ap, validResponse ? statusCodeToPrimResultCode(statusCode) : PRC_REFUSED);

delete packet;
}
Expand Down
2 changes: 1 addition & 1 deletion src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class INET_API Ieee80211MgmtSta : public Ieee80211MgmtBase
double rxPower;

bool isAuthenticated;
int authSeqExpected; // valid while authenticating; values: 1,3,5...
int authSeqExpected; // Open System response transaction: 2
cMessage *authTimeoutMsg; // if non-nullptr: authentication is in progress

ApInfo()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tkenv-plugin-path = ../../../etc/plugins
**.ap${i}.wlan[*].radio.channelNumber = ${i+1}
</#list>
**.ap*.wlan[*].mgmt.beaconInterval = 100ms
**.wlan[*].mgmt.numAuthSteps = 4
**.wlan[*].mgmt.numAuthSteps = 2

**.mgmt.frameCapacity = 10

Expand Down
117 changes: 117 additions & 0 deletions tests/module/Ieee80211OpenSystemAuthentication_1.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
%description:
Open System rejects unsupported algorithm/transaction inputs without authenticating
peers. STA completes using wire fields, independent of deprecated isLast metadata.

%file: TestAuthentication.cc
#include <iostream>
#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h"
#include "inet/linklayer/ieee80211/mgmt/Ieee80211MgmtSta.h"
namespace inet { namespace ieee80211 {
class TestAuthenticationAp : public Ieee80211MgmtAp {
protected:
Ptr<const Ieee80211AuthenticationFrame> response;
virtual void sendManagementFrame(const char *, const Ptr<Ieee80211MgmtFrame>& body, int subtype, const MacAddress&, uint64_t = 0) override {
if (subtype == ST_AUTHENTICATION) response = dynamicPtrCast<const Ieee80211AuthenticationFrame>(body);
}
virtual void initialize(int stage) override {
Ieee80211MgmtAp::initialize(stage);
if (stage != INITSTAGE_LAST) return;
const MacAddress peer("02:00:00:00:00:99");
for (auto input : {std::pair<int,int>(1,1), {0,3}, {0,1}}) {
auto packet = new Packet("auth");
auto body = makeShared<Ieee80211AuthenticationFrame>();
body->setAlgorithmNumber(input.first);
body->setSequenceNumber(input.second);
packet->insertAtBack(body);
auto header = makeShared<Ieee80211MgmtHeader>();
header->setType(ST_AUTHENTICATION);
header->setTransmitterAddress(peer);
header->setReceiverAddress(mib->address);
handleAuthenticationFrame(packet, header);
ASSERT(response && response->getAlgorithmNumber() == input.first && response->getSequenceNumber() == 2);
auto status = input.first != 0 ? SC_AUTH_ALG0_UNSUP : input.second != 1 ? SC_AUTH_OUT_OF_SEQ : SC_SUCCESSFUL;
ASSERT(response->getStatusCode() == status);
if (status != SC_SUCCESSFUL)
ASSERT(mib->bssAccessPointData.stations.count(peer) == 0);
else
ASSERT(mib->bssAccessPointData.stations.at(peer) == Ieee80211Mib::AUTHENTICATED);
}
std::cout << "AP accepts only Open System transaction1 and responds with transaction2.\n";
}
};
Define_Module(TestAuthenticationAp);
class TestAuthenticationSta : public Ieee80211MgmtSta {
protected:
Ieee80211PrimResultCode result = PRC_SUCCESS;
virtual void sendAuthenticationConfirm(ApInfo *, Ieee80211PrimResultCode code) override { result = code; }
virtual void initialize(int stage) override {
Ieee80211MgmtSta::initialize(stage);
if (stage != INITSTAGE_LAST) return;
const MacAddress peer("02:00:00:00:00:98");
apList.emplace_back();
auto& ap = apList.back();
ap.address = peer;
for (int variant = 0; variant < 4; variant++) {
ap.isAuthenticated = false;
ap.authSeqExpected = 2;
ap.authTimeoutMsg = new cMessage("authTimeout");
scheduleAfter(1, ap.authTimeoutMsg);
auto packet = new Packet("auth-response");
auto body = makeShared<Ieee80211AuthenticationFrame>();
body->setAlgorithmNumber(variant == 0 ? 1 : 0);
body->setSequenceNumber(variant == 1 ? 4 : 2);
body->setStatusCode(variant == 2 ? SC_UNSPECIFIED : SC_SUCCESSFUL);
body->setIsLast(false); // Must not trigger a third frame.
packet->insertAtBack(body);
auto header = makeShared<Ieee80211MgmtHeader>();
header->setType(ST_AUTHENTICATION);
header->setTransmitterAddress(peer);
handleAuthenticationFrame(packet, header);
ASSERT(ap.isAuthenticated == (variant == 3));
ASSERT(ap.authTimeoutMsg == nullptr);
ASSERT((result == PRC_SUCCESS) == (variant == 3));
}
std::cout << "STA completion requires successful algorithm0 transaction2, without isLast.\n";
}
};
Define_Module(TestAuthenticationSta);
}}

%file: test.ned
import inet.linklayer.ieee80211.mgmt.Ieee80211MgmtAp;
import inet.linklayer.ieee80211.mgmt.Ieee80211MgmtSta;
import inet.node.wireless.AccessPoint;
import inet.node.inet.WirelessHost;
import inet.physicallayer.wireless.ieee80211.packetlevel.Ieee80211ScalarRadioMedium;
simple TestAuthenticationAp extends Ieee80211MgmtAp { parameters: @class(::inet::ieee80211::TestAuthenticationAp); }
simple TestAuthenticationSta extends Ieee80211MgmtSta { parameters: @class(::inet::ieee80211::TestAuthenticationSta); }
network AuthenticationNetwork {
submodules:
radioMedium: Ieee80211ScalarRadioMedium;
ap: AccessPoint;
sta: WirelessHost;
}

%inifile: omnetpp.ini
[General]
network = AuthenticationNetwork
ned-path = .;../../../../src;../../lib
sim-time-limit = 1ms
seed-set = 0
record-vector-results = false
record-scalar-results = false
*.ap.wlan[0].mgmt.typename = "TestAuthenticationAp"
*.sta.wlan[0].mgmt.typename = "TestAuthenticationSta"
*.sta.wlan[0].agent.startingTime = 2s
**.opMode = "g(erp)"
**.bitrate = 6Mbps
**.mobility.initFromDisplayString = false
**.mobility.initialX = 0m
**.mobility.initialY = 0m
**.mobility.initialZ = 0m

%contains: stdout
AP accepts only Open System transaction1 and responds with transaction2.

%contains: stdout
STA completion requires successful algorithm0 transaction2, without isLast.
Loading
Loading