Skip to content

Commit 928c236

Browse files
fix(ieee80211): make Block Ack receive processing transactional
Update the Block Ack scoreboard for every related received QoS Data MPDU, including frames using Normal Ack, independently of reorder-buffer admission. Handle the receive-window cases explicitly: ignore old sequence numbers, record in-window MPDUs, and advance WinStartR before recording MPDUs beyond WinEndR. This behavior deliberately applies to both Basic and Compressed Block Ack agreements, as required by IEEE 802.11-2024 sections 10.25.6.3 and 10.25.6.4. Make far-ahead reorder-window movement transactional. Validate and insert the incoming MPDU against the proposed WinStartB before releasing displaced MSDUs or changing NextExpectedSequenceNumber. If admission fails, discard only the incoming packet and preserve the reorder window and its buffered frames, while retaining the independently required scoreboard update. Calculate receive-buffer capacity after accounting for entries displaced by the proposed window. Count each fragment as one buffer slot, allow an advancing MPDU to reuse slots that will be reclaimed, and remove incomplete stale entries only after successful admission. Preserve delivery of complete displaced and consecutive MSDUs, including across the 4095-to-0 sequence number boundary. Centralize supported one-TID BlockAckReq classification and extraction of the variant, TID, and starting sequence number. Use the shared classifier in HCF, RecipientQosMacDataService, and BlockAckReordering so only Basic and Compressed requests reach the implemented paths. Multi-TID requests remain unsupported and follow the existing rejection behavior. Extend the focused Block Ack tests to cover old scoreboard inputs, Normal-Ack behavior with Basic Block Ack, fragment-full admission failure, successful slot reclamation across sequence wrap, real HCF dispatch through a procedure spy, and consistent Basic, Compressed, and Multi-TID request classification.
1 parent 707ae0b commit 928c236

8 files changed

Lines changed: 267 additions & 77 deletions

File tree

src/inet/linklayer/ieee80211/mac/blockack/BlockAckRecord.cc

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ void BlockAckRecord::dataFrameReceived(const Ptr<const Ieee80211DataHeader>& hea
2323
{
2424
SequenceNumberCyclic sequenceNumber = header->getSequenceNumber();
2525
FragmentNumber fragmentNumber = header->getFragmentNumber();
26-
// IEEE Std 802.11-2024, 10.25.6.3(b) and 10.25.6.4(c): a related
27-
// MPDU beyond WinEndR advances the receive window before its bit is set.
26+
// IEEE Std 802.11-2024, 10.25.6.3(b), case 3: an old related MPDU
27+
// does not change the Block Ack record.
28+
if (!(startingSequenceNumber <= sequenceNumber && sequenceNumber < startingSequenceNumber + 2048))
29+
return;
30+
// Cases 1 and 2: record an in-window MPDU, or advance WinStartR
31+
// before recording an MPDU beyond WinEndR (also see 10.25.6.4(c)).
2832
if (startingSequenceNumber + windowSize <= sequenceNumber && sequenceNumber < startingSequenceNumber + 2048)
2933
advanceStartingSequenceNumber(sequenceNumber - windowSize + 1);
3034
acknowledgmentState[SequenceControlField(sequenceNumber.get(), fragmentNumber)] = true;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// Copyright (C) 2026 OpenSim Ltd.
3+
//
4+
// SPDX-License-Identifier: LGPL-3.0-or-later
5+
//
6+
7+
#ifndef __INET_ONETIDBLOCKACKREQVARIANT_H
8+
#define __INET_ONETIDBLOCKACKREQVARIANT_H
9+
10+
#include <optional>
11+
12+
#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h"
13+
14+
namespace inet {
15+
namespace ieee80211 {
16+
17+
enum class OneTidBlockAckReqVariant
18+
{
19+
BASIC,
20+
COMPRESSED,
21+
};
22+
23+
struct OneTidBlockAckReqDetails
24+
{
25+
Ptr<const Ieee80211BlockAckReq> blockAckReq;
26+
OneTidBlockAckReqVariant variant;
27+
Tid tid;
28+
SequenceNumberCyclic startingSequenceNumber;
29+
};
30+
31+
inline std::optional<OneTidBlockAckReqDetails> getOneTidBlockAckReqDetails(const Ptr<const Ieee80211MacHeader>& header)
32+
{
33+
if (auto basicBlockAckReq = dynamicPtrCast<const Ieee80211BasicBlockAckReq>(header))
34+
return OneTidBlockAckReqDetails { basicBlockAckReq, OneTidBlockAckReqVariant::BASIC,
35+
static_cast<Tid>(basicBlockAckReq->getTidInfo()), basicBlockAckReq->getStartingSequenceNumber() };
36+
else if (auto compressedBlockAckReq = dynamicPtrCast<const Ieee80211CompressedBlockAckReq>(header))
37+
return OneTidBlockAckReqDetails { compressedBlockAckReq, OneTidBlockAckReqVariant::COMPRESSED,
38+
static_cast<Tid>(compressedBlockAckReq->getTidInfo()), compressedBlockAckReq->getStartingSequenceNumber() };
39+
else
40+
return std::nullopt;
41+
}
42+
43+
} // namespace ieee80211
44+
} // namespace inet
45+
46+
#endif

src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include "inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.h"
99

10+
#include "inet/linklayer/ieee80211/mac/blockack/OneTidBlockAckReqVariant.h"
1011
#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h"
1112

1213
namespace inet {
@@ -22,10 +23,17 @@ BlockAckReordering::ReorderBuffer BlockAckReordering::processReceivedQoSFrame(Re
2223
auto sequenceNumber = dataHeader->getSequenceNumber();
2324
auto startingSequenceNumber = receiveBuffer->getNextExpectedSequenceNumber();
2425
bool advancesWindow = startingSequenceNumber + receiveBuffer->getBufferSize() <= sequenceNumber && sequenceNumber < startingSequenceNumber + 2048;
26+
// IEEE Std 802.11-2024, 10.25.6.3 and 10.25.6.4: update the
27+
// scoreboard for every related Data frame, independently of reorder storage.
28+
agreement->dataFrameReceived(dataHeader);
2529
if (advancesWindow) {
26-
// IEEE Std 802.11-2024, 10.25.6.6.2.1(b): move WinStartB so the
27-
// future MPDU fits, preserving complete displaced MSDUs for delivery.
30+
// IEEE Std 802.11-2024, 10.25.6.6.2.1(b): store the future MPDU
31+
// before moving WinStartB and releasing complete displaced MSDUs.
2832
auto newStartingSequenceNumber = sequenceNumber - receiveBuffer->getBufferSize() + 1;
33+
if (!receiveBuffer->insertFrame(dataPacket, dataHeader, newStartingSequenceNumber)) {
34+
delete dataPacket;
35+
return framesToPassUp;
36+
}
2937
framesToPassUp = collectCompletePrecedingMpdus(receiveBuffer, newStartingSequenceNumber);
3038
for (const auto& entry : framesToPassUp)
3139
receiveBuffer->removeFrame(SequenceNumberCyclic(entry.first));
@@ -37,34 +45,33 @@ BlockAckReordering::ReorderBuffer BlockAckReordering::processReceivedQoSFrame(Re
3745
// recipient to reset the timer to detect Block Ack timeout (see 10.5.4).
3846
// This allows the recipient to delete the Block Ack if the originator does not switch
3947
// back to using Block Ack.
40-
if (receiveBuffer->insertFrame(dataPacket, dataHeader)) {
41-
agreement->dataFrameReceived(dataHeader);
42-
if (advancesWindow) {
43-
auto consecutiveCompleteMpdus = collectConsecutiveCompleteFollowingMpdus(receiveBuffer, receiveBuffer->getNextExpectedSequenceNumber());
44-
releaseReceiveBuffer(receiveBuffer, consecutiveCompleteMpdus);
45-
framesToPassUp.insert(framesToPassUp.end(), consecutiveCompleteMpdus.begin(), consecutiveCompleteMpdus.end());
46-
return framesToPassUp;
48+
if (!advancesWindow && !receiveBuffer->insertFrame(dataPacket, dataHeader)) {
49+
delete dataPacket;
50+
return framesToPassUp;
51+
}
52+
if (advancesWindow) {
53+
auto consecutiveCompleteMpdus = collectConsecutiveCompleteFollowingMpdus(receiveBuffer, receiveBuffer->getNextExpectedSequenceNumber());
54+
releaseReceiveBuffer(receiveBuffer, consecutiveCompleteMpdus);
55+
framesToPassUp.insert(framesToPassUp.end(), consecutiveCompleteMpdus.begin(), consecutiveCompleteMpdus.end());
56+
return framesToPassUp;
57+
}
58+
auto earliestCompleteMsduOrAMsdu = getEarliestCompleteMsduOrAMsduIfExists(receiveBuffer);
59+
if (earliestCompleteMsduOrAMsdu.size() > 0) {
60+
auto earliestSequenceNumber = earliestCompleteMsduOrAMsdu.at(0)->peekAtFront<Ieee80211DataHeader>()->getSequenceNumber();
61+
// If, after an MPDU is received, the receive buffer is full, the complete MSDU or A-MSDU with the earliest
62+
// sequence number shall be passed up to the next MAC process.
63+
if (receiveBuffer->isFull()) {
64+
passedUp(receiveBuffer, earliestSequenceNumber);
65+
return ReorderBuffer({ std::make_pair(earliestSequenceNumber.get(), Fragments(earliestCompleteMsduOrAMsdu)) });
4766
}
48-
auto earliestCompleteMsduOrAMsdu = getEarliestCompleteMsduOrAMsduIfExists(receiveBuffer);
49-
if (earliestCompleteMsduOrAMsdu.size() > 0) {
50-
auto earliestSequenceNumber = earliestCompleteMsduOrAMsdu.at(0)->peekAtFront<Ieee80211DataHeader>()->getSequenceNumber();
51-
// If, after an MPDU is received, the receive buffer is full, the complete MSDU or A-MSDU with the earliest
52-
// sequence number shall be passed up to the next MAC process.
53-
if (receiveBuffer->isFull()) {
54-
passedUp(receiveBuffer, earliestSequenceNumber);
55-
return ReorderBuffer({ std::make_pair(earliestSequenceNumber.get(), Fragments(earliestCompleteMsduOrAMsdu)) });
56-
}
57-
// If, after an MPDU is received, the receive buffer is not full, but the sequence number of the complete MSDU or
58-
// A-MSDU in the buffer with the lowest sequence number is equal to the NextExpectedSequenceNumber for
59-
// that Block Ack agreement, then the MPDU shall be passed up to the next MAC process.
60-
else if (earliestSequenceNumber == receiveBuffer->getNextExpectedSequenceNumber()) {
61-
passedUp(receiveBuffer, earliestSequenceNumber);
62-
return ReorderBuffer({ std::make_pair(earliestSequenceNumber.get(), Fragments(earliestCompleteMsduOrAMsdu)) });
63-
}
67+
// If, after an MPDU is received, the receive buffer is not full, but the sequence number of the complete MSDU or
68+
// A-MSDU in the buffer with the lowest sequence number is equal to the NextExpectedSequenceNumber for
69+
// that Block Ack agreement, then the MPDU shall be passed up to the next MAC process.
70+
else if (earliestSequenceNumber == receiveBuffer->getNextExpectedSequenceNumber()) {
71+
passedUp(receiveBuffer, earliestSequenceNumber);
72+
return ReorderBuffer({ std::make_pair(earliestSequenceNumber.get(), Fragments(earliestCompleteMsduOrAMsdu)) });
6473
}
6574
}
66-
else
67-
delete dataPacket;
6875
return framesToPassUp;
6976
}
7077

@@ -75,23 +82,14 @@ BlockAckReordering::ReorderBuffer BlockAckReordering::processReceivedBlockAckReq
7582
{
7683
// The originator shall use the Block Ack starting sequence control to signal the first MPDU in the block for
7784
// which an acknowledgment is expected.
78-
SequenceNumberCyclic startingSequenceNumber;
79-
Tid tid = -1;
80-
if (auto basicReq = dynamicPtrCast<const Ieee80211BasicBlockAckReq>(blockAckReq)) {
81-
tid = basicReq->getTidInfo();
82-
startingSequenceNumber = basicReq->getStartingSequenceNumber();
83-
}
84-
else if (auto compressedReq = dynamicPtrCast<const Ieee80211CompressedBlockAckReq>(blockAckReq)) {
85-
tid = compressedReq->getTidInfo();
86-
startingSequenceNumber = compressedReq->getStartingSequenceNumber();
87-
}
88-
else {
85+
auto blockAckReqDetails = getOneTidBlockAckReqDetails(blockAckReq);
86+
if (!blockAckReqDetails)
8987
throw cRuntimeError("Multi-Tid BlockAckReq is currently an unimplemented feature");
90-
}
88+
auto startingSequenceNumber = blockAckReqDetails->startingSequenceNumber;
9189
// IEEE Std 802.11-2024, 10.25.6.3-10.25.6.5: adjust WinStartR
9290
// from the BAR before generating the response, even without a receive buffer.
9391
agreement->getBlockAckRecord()->advanceStartingSequenceNumber(startingSequenceNumber);
94-
auto id = std::make_pair(tid, blockAckReq->getTransmitterAddress());
92+
auto id = std::make_pair(blockAckReqDetails->tid, blockAckReq->getTransmitterAddress());
9593
auto it = receiveBuffers.find(id);
9694
if (it != receiveBuffers.end()) {
9795
ReceiveBuffer *receiveBuffer = it->second;

src/inet/linklayer/ieee80211/mac/blockackreordering/ReceiveBuffer.cc

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,47 @@ ReceiveBuffer::ReceiveBuffer(int bufferSize, SequenceNumberCyclic nextExpectedSe
2222
// data frame, unless the sequence number of the frame is older than the NextExpectedSequenceNumber for that
2323
// Block Ack agreement, in which case the frame is discarded because it is either old or a duplicate.
2424
//
25-
bool ReceiveBuffer::insertFrame(Packet *dataPacket, const Ptr<const Ieee80211DataHeader>& dataHeader)
25+
bool ReceiveBuffer::canInsertFrame(const Ptr<const Ieee80211DataHeader>& dataHeader, SequenceNumberCyclic nextExpectedSequenceNumber) const
2626
{
2727
auto sequenceNumber = dataHeader->getSequenceNumber();
2828
auto fragmentNumber = dataHeader->getFragmentNumber();
29-
// The total number of MPDUs in these MSDUs may not
30-
// exceed the reorder buffer size in the receiver.
31-
if (length < bufferSize && nextExpectedSequenceNumber <= sequenceNumber && sequenceNumber < nextExpectedSequenceNumber + bufferSize) {
32-
auto it = buffer.find(sequenceNumber.get());
33-
if (it != buffer.end()) {
34-
auto& fragments = it->second;
35-
// TODO efficiency
36-
for (auto fragment : fragments) {
37-
const auto& fragmentHeader = fragment->peekAtFront<Ieee80211DataHeader>();
38-
if (fragmentHeader->getSequenceNumber() == sequenceNumber && fragmentHeader->getFragmentNumber() == fragmentNumber)
39-
return false;
40-
}
41-
fragments.push_back(dataPacket);
42-
}
43-
else {
44-
buffer[sequenceNumber.get()].push_back(dataPacket);
29+
if (!(nextExpectedSequenceNumber <= sequenceNumber && sequenceNumber < nextExpectedSequenceNumber + bufferSize))
30+
return false;
31+
int retainedLength = length;
32+
for (const auto& entry : buffer) {
33+
if (SequenceNumberCyclic(entry.first) < nextExpectedSequenceNumber)
34+
retainedLength -= entry.second.size();
35+
}
36+
// IEEE Std 802.11-2024, 9.4.1.13, footnote 26: each fragment
37+
// occupies one receive-buffer slot.
38+
if (retainedLength >= bufferSize)
39+
return false;
40+
auto it = buffer.find(sequenceNumber.get());
41+
if (it != buffer.end()) {
42+
for (auto fragment : it->second) {
43+
const auto& fragmentHeader = fragment->peekAtFront<Ieee80211DataHeader>();
44+
if (fragmentHeader->getSequenceNumber() == sequenceNumber && fragmentHeader->getFragmentNumber() == fragmentNumber)
45+
return false;
4546
}
46-
// The total number of frames that can be sent depends on the total
47-
// number of MPDUs in all the outstanding MSDUs.
48-
length++;
49-
return true;
5047
}
51-
return false;
48+
return true;
49+
}
50+
51+
bool ReceiveBuffer::insertFrame(Packet *dataPacket, const Ptr<const Ieee80211DataHeader>& dataHeader)
52+
{
53+
return insertFrame(dataPacket, dataHeader, nextExpectedSequenceNumber);
54+
}
55+
56+
bool ReceiveBuffer::insertFrame(Packet *dataPacket, const Ptr<const Ieee80211DataHeader>& dataHeader, SequenceNumberCyclic nextExpectedSequenceNumber)
57+
{
58+
if (!canInsertFrame(dataHeader, nextExpectedSequenceNumber))
59+
return false;
60+
auto sequenceNumber = dataHeader->getSequenceNumber();
61+
buffer[sequenceNumber.get()].push_back(dataPacket);
62+
// The total number of frames that can be sent depends on the total
63+
// number of MPDUs in all the outstanding MSDUs.
64+
length++;
65+
return true;
5266
}
5367

5468
void ReceiveBuffer::dropFramesUntil(SequenceNumberCyclic sequenceNumber)

src/inet/linklayer/ieee80211/mac/blockackreordering/ReceiveBuffer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,14 @@ class INET_API ReceiveBuffer
3030
int length = 0;
3131
SequenceNumberCyclic nextExpectedSequenceNumber;
3232

33+
bool canInsertFrame(const Ptr<const Ieee80211DataHeader>& dataHeader, SequenceNumberCyclic nextExpectedSequenceNumber) const;
34+
3335
public:
3436
ReceiveBuffer(int bufferSize, SequenceNumberCyclic nextExpectedSequenceNumber);
3537
virtual ~ReceiveBuffer();
3638

3739
bool insertFrame(Packet *dataPacket, const Ptr<const Ieee80211DataHeader>& dataHeader);
40+
bool insertFrame(Packet *dataPacket, const Ptr<const Ieee80211DataHeader>& dataHeader, SequenceNumberCyclic nextExpectedSequenceNumber);
3841
void dropFramesUntil(SequenceNumberCyclic sequenceNumber);
3942
void removeFrame(SequenceNumberCyclic sequenceNumber);
4043
Fragments extractFrames();

src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "inet/common/ModuleAccess.h"
1111
#include "inet/linklayer/ieee80211/mac/Ieee80211Mac.h"
1212
#include "inet/linklayer/ieee80211/mac/blockack/Ieee80211AddbaTransactionTag_m.h"
13+
#include "inet/linklayer/ieee80211/mac/blockack/OneTidBlockAckReqVariant.h"
1314
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h"
1415
#include "inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckProcedure.h"
1516
#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h"
@@ -519,9 +520,9 @@ void Hcf::recipientProcessReceivedControlFrame(Packet *packet, const Ptr<const I
519520
{
520521
if (auto rtsFrame = dynamicPtrCast<const Ieee80211RtsFrame>(header))
521522
ctsProcedure->processReceivedRts(packet, rtsFrame, ctsPolicy, this);
522-
else if (auto blockAckRequest = dynamicPtrCast<const Ieee80211BlockAckReq>(header)) {
523+
else if (auto blockAckReqDetails = getOneTidBlockAckReqDetails(header)) {
523524
if (recipientBlockAckProcedure)
524-
recipientBlockAckProcedure->processReceivedBlockAckReq(packet, blockAckRequest, recipientAckPolicy, recipientBlockAckAgreementHandler, this);
525+
recipientBlockAckProcedure->processReceivedBlockAckReq(packet, blockAckReqDetails->blockAckReq, recipientAckPolicy, recipientBlockAckAgreementHandler, this);
525526
}
526527
else if (dynamicPtrCast<const Ieee80211AckFrame>(header))
527528
EV_WARN << "ACK frame received after timeout, ignoring it.\n"; // drop it, it is an ACK frame that is received after the ACKTimeout

src/inet/linklayer/ieee80211/mac/recipient/RecipientQosMacDataService.cc

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "inet/common/Simsignals.h"
1111
#include "inet/linklayer/ieee80211/mac/aggregation/MpduDeaggregation.h"
1212
#include "inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.h"
13+
#include "inet/linklayer/ieee80211/mac/blockack/OneTidBlockAckReqVariant.h"
1314
#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreementHandler.h"
1415
#include "inet/linklayer/ieee80211/mac/duplicateremoval/QosDuplicateRemoval.h"
1516
#include "inet/linklayer/ieee80211/mac/fragmentation/BasicReassembly.h"
@@ -151,20 +152,13 @@ IRecipientQosMacDataService::ManagementFrameReceptionResult RecipientQosMacDataS
151152
std::vector<Packet *> RecipientQosMacDataService::controlFrameReceived(Packet *controlPacket, const Ptr<const Ieee80211MacHeader>& controlHeader, IRecipientBlockAckAgreementHandler *blockAckAgreementHandler)
152153
{
153154
Enter_Method("controlFrameReceived");
154-
if (auto blockAckReq = dynamicPtrCast<const Ieee80211BlockAckReq>(controlHeader)) {
155+
if (auto blockAckReqDetails = getOneTidBlockAckReqDetails(controlHeader)) {
155156
BlockAckReordering::ReorderBuffer frames;
156157
if (blockAckReordering) {
157-
Tid tid = -1;
158-
if (auto basicBlockAckReq = dynamicPtrCast<const Ieee80211BasicBlockAckReq>(blockAckReq))
159-
tid = basicBlockAckReq->getTidInfo();
160-
else if (auto compressedBlockAckReq = dynamicPtrCast<const Ieee80211CompressedBlockAckReq>(blockAckReq))
161-
tid = compressedBlockAckReq->getTidInfo();
162-
else
163-
return std::vector<Packet *>();
164-
MacAddress originatorAddr = blockAckReq->getTransmitterAddress();
165-
RecipientBlockAckAgreement *agreement = blockAckAgreementHandler->getAgreement(tid, originatorAddr);
158+
MacAddress originatorAddr = blockAckReqDetails->blockAckReq->getTransmitterAddress();
159+
RecipientBlockAckAgreement *agreement = blockAckAgreementHandler->getAgreement(blockAckReqDetails->tid, originatorAddr);
166160
if (agreement)
167-
frames = blockAckReordering->processReceivedBlockAckReq(agreement, blockAckReq);
161+
frames = blockAckReordering->processReceivedBlockAckReq(agreement, blockAckReqDetails->blockAckReq);
168162
else
169163
return std::vector<Packet *>();
170164
}

0 commit comments

Comments
 (0)