1717#include " inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
1818#include " inet/linklayer/ieee80211/mac/Ieee80211SubtypeTag_m.h"
1919#include " inet/linklayer/ieee80211/mgmt/Ieee80211MgmtAp.h"
20+ #include " inet/linklayer/ieee80211/mgmt/Ieee80211HtMgmtElements.h"
2021#include " inet/networklayer/common/NetworkInterface.h"
2122#include " inet/physicallayer/wireless/ieee80211/packetlevel/Ieee80211Radio.h"
2223
@@ -93,6 +94,7 @@ void Ieee80211MgmtAp::receiveSignal(cComponent *source, simsignal_t signalID, in
9394 if (signalID == Ieee80211Radio::radioChannelChangedSignal) {
9495 EV << " updating channel number\n " ;
9596 channelNumber = value;
97+ mib->htOperation .primaryChannel = channelNumber;
9698 }
9799}
98100
@@ -105,16 +107,50 @@ void Ieee80211MgmtAp::receiveSignal(cComponent *source, simsignal_t signalID, cO
105107 if (context->getNumSteps () >= 2 ) {
106108 auto transmitStep = dynamic_cast <ITransmitStep *>(context->getStepBeforeLast ());
107109 auto receiveStep = dynamic_cast <IReceiveStep *>(context->getLastStep ());
108- if (transmitStep && receiveStep &&
109- transmitStep->getCompletion () == IFrameSequenceStep::Completion::ACCEPTED &&
110- receiveStep->getCompletion () == IFrameSequenceStep::Completion::ACCEPTED ) {
110+ if (transmitStep && receiveStep) {
111111 auto responseHeader = dynamicPtrCast<const Ieee80211MgmtHeader>(transmitStep->getFrameToTransmit ()->peekAtFront <Ieee80211MacHeader>());
112112 if (responseHeader != nullptr && (responseHeader->getType () == ST_ASSOCIATIONRESPONSE || responseHeader->getType () == ST_REASSOCIATIONRESPONSE )) {
113- auto ackHeader = receiveStep->getReceivedFrame ()->peekAtFront <Ieee80211MacHeader>();
114- if (ackHeader->getType () == ST_ACK ) {
115- if (responseHeader->getType () == ST_ASSOCIATIONRESPONSE && mib->bssAccessPointData .stations [responseHeader->getReceiverAddress ()] != Ieee80211Mib::ASSOCIATED )
116- sendAssocNotification (responseHeader->getReceiverAddress ());
117- mib->bssAccessPointData .stations [responseHeader->getReceiverAddress ()] = Ieee80211Mib::ASSOCIATED ;
113+ const auto & address = responseHeader->getReceiverAddress ();
114+ auto sta = staList.find (address);
115+ bool exchangeSucceeded = transmitStep->getCompletion () == IFrameSequenceStep::Completion::ACCEPTED &&
116+ receiveStep->getCompletion () == IFrameSequenceStep::Completion::ACCEPTED &&
117+ receiveStep->getReceivedFrame ()->peekAtFront <Ieee80211MacHeader>()->getType () == ST_ACK ;
118+ bool isPendingResponse = sta != staList.end () && sta->second .pendingAssociationResponse == transmitStep->getFrameToTransmit ();
119+ if (isPendingResponse && sta->second .pendingAssociationSuccessful && exchangeSucceeded) {
120+ bool wasAssociated = mib->bssAccessPointData .stations [address] == Ieee80211Mib::ASSOCIATED ;
121+ mib->bssAccessPointData .associationIds [address] = sta->second .pendingAssociationId ;
122+ mib->bssAccessPointData .stations [address] = Ieee80211Mib::ASSOCIATED ;
123+ if (sta->second .pendingHtStateAvailable ) {
124+ // IEEE Std 802.11-2024 association state becomes effective only after the successful response exchange.
125+ if (sta->second .pendingHtCapabilitiesValid )
126+ mib->setPeerHtCapabilities (address, sta->second .pendingHtCapabilities , mib->htOperation );
127+ else
128+ mib->removePeerHtCapabilities (address);
129+ }
130+ // Signal delivery is synchronous; observers must see committed station and peer state.
131+ if (responseHeader->getType () == ST_ASSOCIATIONRESPONSE && !wasAssociated)
132+ sendAssocNotification (address);
133+ }
134+ else if (isPendingResponse && !sta->second .pendingAssociationSuccessful && exchangeSucceeded &&
135+ mib->bssAccessPointData .stations [address] == Ieee80211Mib::ASSOCIATED )
136+ {
137+ // This model does not implement negotiated management-frame protection.
138+ // IEEE Std 802.11-2024, 11.3.5.3(p): a refused (re)association
139+ // therefore moves the STA from State 4 back to State 3.
140+ mib->releaseAssociationId (address);
141+ mib->bssAccessPointData .stations [address] = Ieee80211Mib::AUTHENTICATED ;
142+ // Signal delivery is synchronous; observers must see the downgraded state.
143+ sendDisAssocNotification (address);
144+ }
145+ if (isPendingResponse) {
146+ bool retryPending = false ;
147+ if (!exchangeSucceeded) {
148+ auto inProgressFrames = context->getInProgressFrames ();
149+ for (int i = 0 ; i < inProgressFrames->getLength (); i++)
150+ retryPending |= inProgressFrames->getFrames (i) == transmitStep->getFrameToTransmit ();
151+ }
152+ if (exchangeSucceeded || !retryPending)
153+ clearPendingAssociation (&sta->second );
118154 }
119155 }
120156 }
@@ -130,13 +166,42 @@ Ieee80211MgmtAp::StaInfo *Ieee80211MgmtAp::lookupSenderSTA(const Ptr<const Ieee8
130166 return it == staList.end () ? nullptr : &(it->second );
131167}
132168
133- void Ieee80211MgmtAp::sendManagementFrame (const char *name, const Ptr<Ieee80211MgmtFrame>& body, int subtype, const MacAddress& destAddr)
169+ Packet * Ieee80211MgmtAp::sendManagementFrame (const char *name, const Ptr<Ieee80211MgmtFrame>& body, int subtype, const MacAddress& destAddr)
134170{
135171 auto packet = new Packet (name);
136172 packet->addTag <MacAddressReq>()->setDestAddress (destAddr);
137173 packet->addTag <Ieee80211SubtypeReq>()->setSubtype (subtype);
138174 packet->insertAtBack (body);
139175 sendDown (packet);
176+ return packet;
177+ }
178+
179+ short Ieee80211MgmtAp::reserveAssociationId (StaInfo *sta) const
180+ {
181+ auto existing = mib->bssAccessPointData .associationIds .find (sta->address );
182+ if (existing != mib->bssAccessPointData .associationIds .end ())
183+ return existing->second ;
184+ if (sta->pendingAssociationId != 0 )
185+ return sta->pendingAssociationId ;
186+ for (short aid = 1 ; aid <= 2007 ; aid++) {
187+ bool used = false ;
188+ for (const auto & entry : mib->bssAccessPointData .associationIds )
189+ used |= entry.second == aid;
190+ for (const auto & entry : staList)
191+ used |= entry.second .pendingAssociationId == aid;
192+ if (!used)
193+ return aid;
194+ }
195+ throw cRuntimeError (" No IEEE 802.11 association ID is available" );
196+ }
197+
198+ void Ieee80211MgmtAp::clearPendingAssociation (StaInfo *sta)
199+ {
200+ sta->pendingAssociationSuccessful = false ;
201+ sta->pendingAssociationId = 0 ;
202+ sta->pendingAssociationResponse = nullptr ;
203+ sta->pendingHtStateAvailable = false ;
204+ sta->pendingHtCapabilitiesValid = false ;
140205}
141206
142207void Ieee80211MgmtAp::sendBeacon ()
@@ -147,7 +212,9 @@ void Ieee80211MgmtAp::sendBeacon()
147212 body->setSupportedRates (supportedRates);
148213 body->setBeaconInterval (beaconInterval);
149214 body->setChannelNumber (channelNumber);
150- body->setChunkLength (B (8 + 2 + 2 + (2 + ssid.length ()) + (2 + supportedRates.numRates )));
215+ addHtCapabilities (body);
216+ addHtOperation (body);
217+ body->setChunkLength (B (8 + 2 + 2 + (2 + ssid.length ()) + (2 + supportedRates.numRates )) + getHtMgmtElementsLength (body));
151218 sendManagementFrame (" Beacon" , body, ST_BEACON , MacAddress::BROADCAST_ADDRESS );
152219}
153220
@@ -165,6 +232,7 @@ void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr<const
165232 sta->address = staAddress;
166233 mib->bssAccessPointData .stations [staAddress] = Ieee80211Mib::NOT_AUTHENTICATED ;
167234 sta->authSeqExpected = 1 ;
235+ clearPendingAssociation (sta);
168236 }
169237
170238 // reset authentication status, when starting a new auth sequence
@@ -180,6 +248,8 @@ void Ieee80211MgmtAp::handleAuthenticationFrame(Packet *packet, const Ptr<const
180248 mib->releaseAssociationId (sta->address );
181249 }
182250 mib->bssAccessPointData .stations [sta->address ] = Ieee80211Mib::NOT_AUTHENTICATED ;
251+ clearPendingAssociation (sta);
252+ mib->removePeerHtCapabilities (sta->address );
183253 sta->authSeqExpected = 1 ;
184254 }
185255
@@ -240,6 +310,8 @@ void Ieee80211MgmtAp::handleDeauthenticationFrame(Packet *packet, const Ptr<cons
240310 }
241311 mib->bssAccessPointData .stations [sta->address ] = Ieee80211Mib::NOT_AUTHENTICATED ;
242312 sta->authSeqExpected = 1 ;
313+ clearPendingAssociation (sta);
314+ mib->removePeerHtCapabilities (sta->address );
243315 }
244316}
245317
@@ -258,15 +330,27 @@ void Ieee80211MgmtAp::handleAssociationRequestFrame(Packet *packet, const Ptr<co
258330 return ;
259331 }
260332
333+ const auto & requestBody = packet->peekData <Ieee80211AssociationRequestFrame>();
334+ sta->pendingAssociationSuccessful = false ;
335+ sta->pendingHtStateAvailable = true ;
336+ sta->pendingHtCapabilitiesValid = mib->isHtOperationSupported () && requestBody->getHtCapabilitiesPresent ();
337+ if (sta->pendingHtCapabilitiesValid )
338+ sta->pendingHtCapabilities = makeHtCapabilities (requestBody->getHtCapabilities ());
339+ bool basicHtMcsSupported = !sta->pendingHtCapabilitiesValid ||
340+ supportsBasicHtMcsSet (sta->pendingHtCapabilities , mib->htOperation );
261341 delete packet;
262342
263- // send OK response
343+ // IEEE Std 802.11-2024, 11.3.5.3 g): an HT STA must support every Basic HT-MCS.
264344 const auto & body = makeShared<Ieee80211AssociationResponseFrame>();
265- body->setStatusCode (SC_SUCCESSFUL );
266- body->setAid (mib->allocateAssociationId (sta->address ));
345+ body->setStatusCode (basicHtMcsSupported ? SC_SUCCESSFUL : SC_DATARATE_UNSUP );
346+ sta->pendingAssociationId = basicHtMcsSupported ? reserveAssociationId (sta) : 0 ;
347+ body->setAid (sta->pendingAssociationId );
348+ sta->pendingAssociationSuccessful = basicHtMcsSupported;
267349 body->setSupportedRates (supportedRates);
268- body->setChunkLength (B (2 + 2 + 2 + body->getSupportedRates ().numRates + 2 ));
269- sendManagementFrame (" AssocResp-OK" , body, ST_ASSOCIATIONRESPONSE , sta->address );
350+ addHtCapabilities (body);
351+ addHtOperation (body);
352+ body->setChunkLength (B (2 + 2 + 2 + body->getSupportedRates ().numRates + 2 ) + getHtMgmtElementsLength (body));
353+ sta->pendingAssociationResponse = sendManagementFrame (basicHtMcsSupported ? " AssocResp-OK" : " AssocResp-UnsupportedHtMcs" , body, ST_ASSOCIATIONRESPONSE , sta->address );
270354}
271355
272356void Ieee80211MgmtAp::handleAssociationResponseFrame (Packet *packet, const Ptr<const Ieee80211MgmtHeader>& header)
@@ -289,15 +373,27 @@ void Ieee80211MgmtAp::handleReassociationRequestFrame(Packet *packet, const Ptr<
289373 return ;
290374 }
291375
376+ const auto & requestBody = packet->peekData <Ieee80211ReassociationRequestFrame>();
377+ sta->pendingAssociationSuccessful = false ;
378+ sta->pendingHtStateAvailable = true ;
379+ sta->pendingHtCapabilitiesValid = mib->isHtOperationSupported () && requestBody->getHtCapabilitiesPresent ();
380+ if (sta->pendingHtCapabilitiesValid )
381+ sta->pendingHtCapabilities = makeHtCapabilities (requestBody->getHtCapabilities ());
382+ bool basicHtMcsSupported = !sta->pendingHtCapabilitiesValid ||
383+ supportsBasicHtMcsSet (sta->pendingHtCapabilities , mib->htOperation );
292384 delete packet;
293385
294386 // send OK response
295387 const auto & body = makeShared<Ieee80211ReassociationResponseFrame>();
296- body->setStatusCode (SC_SUCCESSFUL );
297- body->setAid (mib->allocateAssociationId (sta->address ));
388+ body->setStatusCode (basicHtMcsSupported ? SC_SUCCESSFUL : SC_DATARATE_UNSUP );
389+ sta->pendingAssociationId = basicHtMcsSupported ? reserveAssociationId (sta) : 0 ;
390+ body->setAid (sta->pendingAssociationId );
391+ sta->pendingAssociationSuccessful = basicHtMcsSupported;
298392 body->setSupportedRates (supportedRates);
299- body->setChunkLength (B (2 + (2 + ssid.length ()) + (2 + supportedRates.numRates ) + 6 ));
300- sendManagementFrame (" ReassocResp-OK" , body, ST_REASSOCIATIONRESPONSE , sta->address );
393+ addHtCapabilities (body);
394+ addHtOperation (body);
395+ body->setChunkLength (B (2 + 2 + 2 + (2 + supportedRates.numRates )) + getHtMgmtElementsLength (body));
396+ sta->pendingAssociationResponse = sendManagementFrame (basicHtMcsSupported ? " ReassocResp-OK" : " ReassocResp-UnsupportedHtMcs" , body, ST_REASSOCIATIONRESPONSE , sta->address );
301397}
302398
303399void Ieee80211MgmtAp::handleReassociationResponseFrame (Packet *packet, const Ptr<const Ieee80211MgmtHeader>& header)
@@ -316,6 +412,8 @@ void Ieee80211MgmtAp::handleDisassociationFrame(Packet *packet, const Ptr<const
316412 mib->releaseAssociationId (sta->address );
317413 }
318414 mib->bssAccessPointData .stations [sta->address ] = Ieee80211Mib::AUTHENTICATED ;
415+ clearPendingAssociation (sta);
416+ mib->removePeerHtCapabilities (sta->address );
319417 }
320418}
321419
@@ -344,7 +442,9 @@ void Ieee80211MgmtAp::handleProbeRequestFrame(Packet *packet, const Ptr<const Ie
344442 body->setSupportedRates (supportedRates);
345443 body->setBeaconInterval (beaconInterval);
346444 body->setChannelNumber (channelNumber);
347- body->setChunkLength (B (8 + 2 + 2 + (2 + ssid.length ()) + (2 + supportedRates.numRates )));
445+ addHtCapabilities (body);
446+ addHtOperation (body);
447+ body->setChunkLength (B (8 + 2 + 2 + (2 + ssid.length ()) + (2 + supportedRates.numRates )) + getHtMgmtElementsLength (body));
348448 sendManagementFrame (" ProbeResp" , body, ST_PROBERESPONSE , staAddress);
349449}
350450
0 commit comments