Skip to content

Commit a8b2362

Browse files
Fix IEEE 802.11 management initialization and AID cleanup
Restore simplified station BSS identity setup to the link-layer initialization stage so automatic network configuration sees the AP SSID, while keeping HT peer capability installation at the final stage after mode-set initialization. Add an AP-owned association-response timeout with per-station deadlines and transaction matching. Expired pending responses now release only uncommitted AID reservations and discard pending HT state without disturbing replacement transactions or committed reassociation IDs. Document the management serializer's exact-body stream contract and extend the HT element regression to cover vendor elements followed by trailing FCS-like bytes. Add deterministic unit and module coverage for timeout scheduling, AID reuse, stale transaction protection, simplified-station wireless identity during network configuration, and final-stage HT peer state. Tests: release and debug builds; 3 focused unit tests; 2 focused module tests.
1 parent c688664 commit a8b2362

9 files changed

Lines changed: 388 additions & 18 deletions

src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.cc

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ Ieee80211MgmtAp::AssociationResponseDisposition Ieee80211MgmtAp::getAssociationR
6666
Ieee80211MgmtAp::~Ieee80211MgmtAp()
6767
{
6868
cancelAndDelete(beaconTimer);
69+
cancelAndDelete(associationResponseTimeoutTimer);
6970
}
7071

7172
void Ieee80211MgmtAp::initialize(int stage)
@@ -76,13 +77,17 @@ void Ieee80211MgmtAp::initialize(int stage)
7677
// read params and init vars
7778
ssid = par("ssid").stdstringValue();
7879
beaconInterval = par("beaconInterval");
80+
associationResponseTimeout = par("associationResponseTimeout");
81+
if (associationResponseTimeout < SIMTIME_ZERO)
82+
throw cRuntimeError("parameter 'associationResponseTimeout' must not be negative");
7983
numAuthSteps = par("numAuthSteps");
8084
if (numAuthSteps != 2 && numAuthSteps != 4)
8185
throw cRuntimeError("parameter 'numAuthSteps' (number of frames exchanged during authentication) must be 2 or 4, not %d", numAuthSteps);
8286
channelNumber = -1; // value will arrive from physical layer in receiveChangeNotification()
8387
WATCH(ssid);
8488
WATCH(channelNumber);
8589
WATCH(beaconInterval);
90+
WATCH(associationResponseTimeout);
8691
WATCH(numAuthSteps);
8792
WATCH(staList);
8893

@@ -95,6 +100,7 @@ void Ieee80211MgmtAp::initialize(int stage)
95100

96101
// start beacon timer (randomize startup time)
97102
beaconTimer = new cMessage("beaconTimer");
103+
associationResponseTimeoutTimer = new cMessage("associationResponseTimeoutTimer");
98104
}
99105
}
100106

@@ -104,6 +110,14 @@ void Ieee80211MgmtAp::handleTimer(cMessage *msg)
104110
sendBeacon();
105111
scheduleAfter(beaconInterval, beaconTimer);
106112
}
113+
else if (msg == associationResponseTimeoutTimer) {
114+
auto sta = staList.find(scheduledAssociationResponseTimeoutAddress);
115+
if (sta != staList.end() && isAssociationResponseTimeoutDue(sta->second,
116+
scheduledAssociationResponseTimeoutTransactionId, scheduledAssociationResponseTimeoutDeadline, simTime()))
117+
clearPendingAssociation(&sta->second);
118+
else
119+
scheduleAssociationResponseTimeout();
120+
}
107121
else {
108122
throw cRuntimeError("internal error: unrecognized timer '%s'", msg->getName());
109123
}
@@ -210,13 +224,46 @@ uint64_t Ieee80211MgmtAp::createAssociationTransactionId()
210224
return nextAssociationTransactionId;
211225
}
212226

227+
bool Ieee80211MgmtAp::isAssociationResponseTimeoutDue(const StaInfo& sta, uint64_t transactionId, simtime_t deadline, simtime_t currentTime)
228+
{
229+
return transactionId != 0 && sta.pendingAssociationTransactionId == transactionId &&
230+
sta.pendingAssociationDeadline == deadline && deadline <= currentTime;
231+
}
232+
213233
void Ieee80211MgmtAp::clearPendingAssociation(StaInfo *sta)
214234
{
215235
mib->cancelAssociationIdReservation(sta->address);
216236
sta->pendingAssociationSuccessful = false;
217237
sta->pendingAssociationTransactionId = 0;
238+
sta->pendingAssociationDeadline = SIMTIME_MAX;
218239
sta->pendingHtStateAvailable = false;
219240
sta->pendingHtCapabilitiesValid = false;
241+
sta->pendingHtCapabilities = Ieee80211HtCapabilities();
242+
scheduleAssociationResponseTimeout();
243+
}
244+
245+
void Ieee80211MgmtAp::scheduleAssociationResponseTimeout()
246+
{
247+
cancelEvent(associationResponseTimeoutTimer);
248+
scheduledAssociationResponseTimeoutTransactionId = 0;
249+
scheduledAssociationResponseTimeoutDeadline = SIMTIME_MAX;
250+
for (const auto& entry : staList) {
251+
const auto& sta = entry.second;
252+
if (sta.pendingAssociationTransactionId != 0 && sta.pendingAssociationDeadline < scheduledAssociationResponseTimeoutDeadline) {
253+
scheduledAssociationResponseTimeoutAddress = entry.first;
254+
scheduledAssociationResponseTimeoutTransactionId = sta.pendingAssociationTransactionId;
255+
scheduledAssociationResponseTimeoutDeadline = sta.pendingAssociationDeadline;
256+
}
257+
}
258+
if (scheduledAssociationResponseTimeoutTransactionId != 0)
259+
scheduleAt(scheduledAssociationResponseTimeoutDeadline, associationResponseTimeoutTimer);
260+
}
261+
262+
void Ieee80211MgmtAp::startAssociationResponseTimeout(StaInfo *sta)
263+
{
264+
ASSERT(sta->pendingAssociationTransactionId != 0);
265+
sta->pendingAssociationDeadline = simTime() + associationResponseTimeout;
266+
scheduleAssociationResponseTimeout();
220267
}
221268

222269
void Ieee80211MgmtAp::sendBeacon()
@@ -247,8 +294,8 @@ void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr<const
247294
sta->address = staAddress;
248295
mib->bssAccessPointData.stations[staAddress] = Ieee80211Mib::NOT_AUTHENTICATED;
249296
sta->authSeqExpected = 1;
250-
clearPendingAssociation(sta);
251297
}
298+
clearPendingAssociation(sta);
252299

253300
// reset authentication status, when starting a new auth sequence
254301
// The statements below are added because the L2 handover time was greater than before when
@@ -263,7 +310,6 @@ void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr<const
263310
mib->releaseAssociationId(sta->address);
264311
}
265312
mib->bssAccessPointData.stations[sta->address] = Ieee80211Mib::NOT_AUTHENTICATED;
266-
clearPendingAssociation(sta);
267313
mib->removePeerHtCapabilities(sta->address);
268314
sta->authSeqExpected = 1;
269315
}
@@ -336,6 +382,8 @@ void Ieee80211MgmtAp::handleAssociationRequestFrame(Packet *packet, const Ptr<co
336382

337383
// "11.3.2 AP association procedures"
338384
StaInfo *sta = lookupSenderSTA(header);
385+
if (sta != nullptr)
386+
clearPendingAssociation(sta);
339387
if (!sta || mib->bssAccessPointData.stations[sta->address] == Ieee80211Mib::NOT_AUTHENTICATED) {
340388
// STA not authenticated: send error and return
341389
const auto& body = makeShared<Ieee80211DeauthenticationFrame>();
@@ -346,7 +394,6 @@ void Ieee80211MgmtAp::handleAssociationRequestFrame(Packet *packet, const Ptr<co
346394
}
347395

348396
const auto& requestBody = packet->peekData<Ieee80211AssociationRequestFrame>();
349-
clearPendingAssociation(sta);
350397
sta->pendingAssociationSuccessful = false;
351398
sta->pendingHtStateAvailable = true;
352399
sta->pendingHtCapabilitiesValid = mib->isHtOperationSupported() && requestBody->getHtCapabilitiesPresent();
@@ -367,6 +414,7 @@ void Ieee80211MgmtAp::handleAssociationRequestFrame(Packet *packet, const Ptr<co
367414
addHtCapabilities(body);
368415
addHtOperation(body);
369416
body->setChunkLength(B(2 + 2 + 2 + body->getSupportedRates().numRates + 2) + getHtMgmtElementsLength(body));
417+
startAssociationResponseTimeout(sta);
370418
sendManagementFrame(basicHtMcsSupported ? "AssocResp-OK" : "AssocResp-UnsupportedHtMcs", body, ST_ASSOCIATIONRESPONSE, sta->address, sta->pendingAssociationTransactionId);
371419
}
372420

@@ -381,6 +429,8 @@ void Ieee80211MgmtAp::handleReassociationRequestFrame(Packet *packet, const Ptr<
381429

382430
// "11.3.4 AP reassociation procedures" -- almost the same as AssociationRequest processing
383431
StaInfo *sta = lookupSenderSTA(header);
432+
if (sta != nullptr)
433+
clearPendingAssociation(sta);
384434
if (!sta || mib->bssAccessPointData.stations[sta->address] == Ieee80211Mib::NOT_AUTHENTICATED) {
385435
// STA not authenticated: send error and return
386436
const auto& body = makeShared<Ieee80211DeauthenticationFrame>();
@@ -391,7 +441,6 @@ void Ieee80211MgmtAp::handleReassociationRequestFrame(Packet *packet, const Ptr<
391441
}
392442

393443
const auto& requestBody = packet->peekData<Ieee80211ReassociationRequestFrame>();
394-
clearPendingAssociation(sta);
395444
sta->pendingAssociationSuccessful = false;
396445
sta->pendingHtStateAvailable = true;
397446
sta->pendingHtCapabilitiesValid = mib->isHtOperationSupported() && requestBody->getHtCapabilitiesPresent();
@@ -412,6 +461,7 @@ void Ieee80211MgmtAp::handleReassociationRequestFrame(Packet *packet, const Ptr<
412461
addHtCapabilities(body);
413462
addHtOperation(body);
414463
body->setChunkLength(B(2 + 2 + 2 + (2 + supportedRates.numRates)) + getHtMgmtElementsLength(body));
464+
startAssociationResponseTimeout(sta);
415465
sendManagementFrame(basicHtMcsSupported ? "ReassocResp-OK" : "ReassocResp-UnsupportedHtMcs", body, ST_REASSOCIATIONRESPONSE, sta->address, sta->pendingAssociationTransactionId);
416466
}
417467

@@ -497,6 +547,7 @@ void Ieee80211MgmtAp::start()
497547
void Ieee80211MgmtAp::stop()
498548
{
499549
cancelEvent(beaconTimer);
550+
cancelEvent(associationResponseTimeoutTimer);
500551
staList.clear();
501552
mib->clearAssociationIds();
502553
Ieee80211MgmtApBase::stop();

src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class INET_API Ieee80211MgmtAp : public Ieee80211MgmtApBase
3838
int authSeqExpected; // when NOT_AUTHENTICATED: transaction sequence number of next expected auth frame
3939
bool pendingAssociationSuccessful = false;
4040
uint64_t pendingAssociationTransactionId = 0;
41+
simtime_t pendingAssociationDeadline = SIMTIME_MAX;
4142
bool pendingHtStateAvailable = false;
4243
bool pendingHtCapabilitiesValid = false;
4344
Ieee80211HtCapabilities pendingHtCapabilities;
@@ -67,12 +68,17 @@ class INET_API Ieee80211MgmtAp : public Ieee80211MgmtApBase
6768
std::string ssid;
6869
int channelNumber = -1;
6970
simtime_t beaconInterval;
71+
simtime_t associationResponseTimeout;
7072
int numAuthSteps = 0;
7173

7274
// state
7375
StaList staList; ///< list of STAs
7476
cMessage *beaconTimer = nullptr;
77+
cMessage *associationResponseTimeoutTimer = nullptr;
7578
uint64_t nextAssociationTransactionId = 0;
79+
MacAddress scheduledAssociationResponseTimeoutAddress;
80+
uint64_t scheduledAssociationResponseTimeoutTransactionId = 0;
81+
simtime_t scheduledAssociationResponseTimeoutDeadline = SIMTIME_MAX;
7682

7783
public:
7884
Ieee80211MgmtAp() {}
@@ -100,8 +106,11 @@ class INET_API Ieee80211MgmtAp : public Ieee80211MgmtApBase
100106

101107
static const Packet *getAssociationResponseFrame(ITransmitStep *transmitStep);
102108
static AssociationResponseDisposition getAssociationResponseDisposition(const Packet *responseFrame, uint64_t pendingTransactionId, bool exchangeSucceeded, bool retryPending);
109+
static bool isAssociationResponseTimeoutDue(const StaInfo& sta, uint64_t transactionId, simtime_t deadline, simtime_t currentTime);
103110
virtual uint64_t createAssociationTransactionId();
104111
virtual void clearPendingAssociation(StaInfo *sta);
112+
virtual void scheduleAssociationResponseTimeout();
113+
virtual void startAssociationResponseTimeout(StaInfo *sta);
105114

106115
/** Utility function: creates and sends a beacon frame */
107116
virtual void sendBeacon();

src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.ned

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ simple Ieee80211MgmtAp extends SimpleModule like IIeee80211Mgmt
2626
@class(Ieee80211MgmtAp);
2727
string ssid = default("SSID");
2828
double beaconInterval @unit(s) = default(100ms);
29+
double associationResponseTimeout @unit(s) = default(5s); // Maximum time to retain an incomplete (re)association response transaction
2930
int numAuthSteps = default(4); // Use 2 for Open System auth, 4 for WEP
3031
string interfaceTableModule;
3132
string radioModule = default("^.radio"); // The path to the Radio module //FIXME remove default value

src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrameSerializer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ namespace ieee80211 {
1919

2020
/**
2121
* Converts between Ieee80211MgmtFrame and binary network byte order IEEE 802.11 mgmt frame.
22+
* The input stream passed to deserialize() must be bounded to the exact management-frame body;
23+
* all bytes remaining after the fixed fields are interpreted as management elements.
2224
*/
2325
class INET_API Ieee80211MgmtFrameSerializer : public FieldsChunkSerializer
2426
{

src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtStaSimplified.cc

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,19 @@ namespace ieee80211 {
1515

1616
Define_Module(Ieee80211MgmtStaSimplified);
1717

18+
static Ieee80211Mib *findAccessPointMib(const MacAddress& accessPointAddress)
19+
{
20+
L3AddressResolver addressResolver;
21+
auto host = addressResolver.findHostWithAddress(accessPointAddress);
22+
if (host == nullptr)
23+
throw cRuntimeError("Access point with address %s not found", accessPointAddress.str().c_str());
24+
auto interfaceTable = addressResolver.findInterfaceTableOf(host);
25+
auto networkInterface = interfaceTable->findInterfaceByAddress(accessPointAddress);
26+
if (networkInterface == nullptr)
27+
throw cRuntimeError("Access point interface with address %s not found", accessPointAddress.str().c_str());
28+
return check_and_cast<Ieee80211Mib *>(networkInterface->getSubmodule("mib"));
29+
}
30+
1831
void Ieee80211MgmtStaSimplified::initialize(int stage)
1932
{
2033
Ieee80211MgmtBase::initialize(stage);
@@ -23,18 +36,16 @@ void Ieee80211MgmtStaSimplified::initialize(int stage)
2336
mib->bssStationData.stationType = Ieee80211Mib::STATION;
2437
mib->bssStationData.isAssociated = true;
2538
}
26-
else if (stage == INITSTAGE_LAST) {
39+
else if (stage == INITSTAGE_LINK_LAYER) {
2740
L3AddressResolver addressResolver;
2841
auto accessPointAddress = addressResolver.resolve(par("accessPointAddress"), L3AddressResolver::ADDR_MAC).toMac();
2942
mib->bssData.bssid = accessPointAddress;
30-
auto host = addressResolver.findHostWithAddress(mib->bssData.bssid);
31-
if (host == nullptr)
32-
throw cRuntimeError("Access point with address %s not found", mib->bssData.bssid.str().c_str());
33-
auto interfaceTable = addressResolver.findInterfaceTableOf(host);
34-
auto networkInterface = interfaceTable->findInterfaceByAddress(mib->bssData.bssid);
35-
auto apMib = dynamic_cast<Ieee80211Mib *>(networkInterface->getSubmodule("mib"));
43+
auto apMib = findAccessPointMib(accessPointAddress);
3644
apMib->bssAccessPointData.stations[mib->address] = Ieee80211Mib::ASSOCIATED;
3745
mib->bssData.ssid = apMib->bssData.ssid;
46+
}
47+
else if (stage == INITSTAGE_LAST) {
48+
auto apMib = findAccessPointMib(mib->bssData.bssid);
3849
// Simplified management is an explicit no-air abstraction: install the state that the
3950
// Association Request/Response exchange would have committed in detailed management.
4051
if (mib->isHtOperationSupported() && apMib->isHtOperationSupported()) {

0 commit comments

Comments
 (0)