Skip to content

Commit ae9af5d

Browse files
Fix IEEE 802.11 ADDBA transaction handling
1 parent fa3c69f commit ae9af5d

21 files changed

Lines changed: 836 additions & 116 deletions

examples/wireless/qos/omnetpp.ini

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,28 @@ sim-time-limit = 10s
8383
extends = MacQos
8484
**.isBlockAckSupported = true
8585

86+
[Config MacQosWithTransactionalBlockAck]
87+
description = "Exercises successful and timed-out ADDBA transactions"
88+
extends = MacQosWithoutAggregation
89+
sim-time-limit = 3s
90+
91+
# Use one voice flow so each wireless hop has a single peer/TID data flow.
92+
*.cliHost.numApps = 1
93+
*.cliHost.app[0].destPort = 5000
94+
*.cliHost.app[0].packetName = "TransactionalBlockAck"
95+
*.cliHost.app[0].startTime = 1s
96+
*.cliHost.app[0].stopTime = 2s
97+
*.cliHost.app[0].sendInterval = 10ms
98+
*.srvHost.numApps = 1
99+
*.srvHost.app[0].localPort = 5000
100+
101+
# The client-to-AP transaction succeeds. The AP-to-server transaction times
102+
# out, because the server deliberately does not support Block Ack.
103+
*.cliHost.wlan[0].mac.hcf.isBlockAckSupported = true
104+
*.ap.wlan[0].mac.hcf.isBlockAckSupported = true
105+
*.srvHost.wlan[0].mac.hcf.isBlockAckSupported = false
106+
**.mac.hcf.originatorAckPolicy.blockAckReqThreshold = 2
107+
**.mac.hcf.originatorBlockAckAgreementPolicy.addbaFailureTimeout = 250ms
108+
86109
# radio medium
87110
*.radioMedium.sameTransmissionStartTimeCheck = "ignore"

src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreement.h

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

1616
class OriginatorBlockAckAgreementHandler;
1717

18+
enum class OriginatorBlockAckAgreementState
19+
{
20+
PENDING,
21+
ESTABLISHED,
22+
};
23+
1824
class INET_API OriginatorBlockAckAgreement : public cObject
1925
{
2026
protected:
@@ -25,19 +31,22 @@ class INET_API OriginatorBlockAckAgreement : public cObject
2531
int bufferSize = -1;
2632
bool isAMsduSupported = false;
2733
bool isDelayedBlockAckPolicySupported = false;
28-
bool isAddbaResponseReceived = false;
34+
OriginatorBlockAckAgreementState state = OriginatorBlockAckAgreementState::PENDING;
2935
bool isAddbaRequestSent = false;
36+
uint8_t dialogToken = 0;
37+
simtime_t addbaResponseDeadline = -1;
3038
simtime_t blockAckTimeoutValue = -1;
3139
simtime_t expirationTime = -1;
3240

3341
public:
34-
OriginatorBlockAckAgreement(MacAddress receiverAddr, Tid tid, SequenceNumberCyclic startingSequenceNumber, int bufferSize, bool isAMsduSupported, bool isDelayedBlockAckPolicySupported) :
42+
OriginatorBlockAckAgreement(MacAddress receiverAddr, Tid tid, SequenceNumberCyclic startingSequenceNumber, int bufferSize, bool isAMsduSupported, bool isDelayedBlockAckPolicySupported, uint8_t dialogToken) :
3543
receiverAddr(receiverAddr),
3644
tid(tid),
3745
startingSequenceNumber(startingSequenceNumber),
3846
bufferSize(bufferSize),
3947
isAMsduSupported(isAMsduSupported),
40-
isDelayedBlockAckPolicySupported(isDelayedBlockAckPolicySupported)
48+
isDelayedBlockAckPolicySupported(isDelayedBlockAckPolicySupported),
49+
dialogToken(dialogToken)
4150
{
4251
}
4352

@@ -46,21 +55,25 @@ class INET_API OriginatorBlockAckAgreement : public cObject
4655
virtual int getBufferSize() const { return bufferSize; }
4756
virtual SequenceNumberCyclic getStartingSequenceNumber() { return startingSequenceNumber; }
4857
virtual void setStartingSequenceNumber(SequenceNumberCyclic sequenceNumber) { startingSequenceNumber = sequenceNumber; }
49-
virtual bool getIsAddbaResponseReceived() const { return isAddbaResponseReceived; }
58+
virtual bool getIsAddbaResponseReceived() const { return state == OriginatorBlockAckAgreementState::ESTABLISHED; }
59+
virtual bool isPending() const { return state == OriginatorBlockAckAgreementState::PENDING; }
5060
virtual bool getIsAddbaRequestSent() const { return isAddbaRequestSent; }
5161
virtual bool getIsAMsduSupported() const { return isAMsduSupported; }
5262
virtual bool getIsDelayedBlockAckPolicySupported() const { return isDelayedBlockAckPolicySupported; }
5363
virtual MacAddress getReceiverAddr() const { return receiverAddr; }
5464
virtual Tid getTid() const { return tid; }
5565
virtual const simtime_t getBlockAckTimeoutValue() const { return blockAckTimeoutValue; }
5666
virtual int getNumSentBaPolicyFrames() const { return numSentBaPolicyFrames; }
67+
virtual uint8_t getDialogToken() const { return dialogToken; }
68+
virtual simtime_t getAddbaResponseDeadline() const { return addbaResponseDeadline; }
5769

5870
virtual void setBufferSize(int bufferSize) { this->bufferSize = bufferSize; }
59-
virtual void setIsAddbaResponseReceived(bool isAddbaResponseReceived) { this->isAddbaResponseReceived = isAddbaResponseReceived; }
71+
virtual void setIsAddbaResponseReceived(bool isAddbaResponseReceived) { state = isAddbaResponseReceived ? OriginatorBlockAckAgreementState::ESTABLISHED : OriginatorBlockAckAgreementState::PENDING; }
6072
virtual void setIsAddbaRequestSent(bool isAddbaRequestSent) { this->isAddbaRequestSent = isAddbaRequestSent; }
6173
virtual void setIsAMsduSupported(bool isAMsduSupported) { this->isAMsduSupported = isAMsduSupported; }
6274
virtual void setIsDelayedBlockAckPolicySupported(bool isDelayedBlockAckPolicySupported) { this->isDelayedBlockAckPolicySupported = isDelayedBlockAckPolicySupported; }
6375
virtual void setBlockAckTimeoutValue(const simtime_t blockAckTimeoutValue) { this->blockAckTimeoutValue = blockAckTimeoutValue; }
76+
virtual void setAddbaResponseDeadline(simtime_t addbaResponseDeadline) { this->addbaResponseDeadline = addbaResponseDeadline; }
6477

6578
virtual void baPolicyFrameSent() { numSentBaPolicyFrames++; }
6679
virtual void calculateExpirationTime() { expirationTime = blockAckTimeoutValue == 0 ? SIMTIME_MAX : simTime() + blockAckTimeoutValue; }
@@ -71,4 +84,3 @@ class INET_API OriginatorBlockAckAgreement : public cObject
7184
} /* namespace inet */
7285

7386
#endif
74-

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

Lines changed: 74 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,19 @@ namespace ieee80211 {
1414

1515
void OriginatorBlockAckAgreementHandler::createAgreement(const Ptr<const Ieee80211AddbaRequest>& addbaRequest)
1616
{
17-
OriginatorBlockAckAgreement *blockAckAgreement = new OriginatorBlockAckAgreement(addbaRequest->getReceiverAddress(), addbaRequest->getTid(), addbaRequest->getStartingSequenceNumber(), addbaRequest->getBufferSize(), addbaRequest->getAMsduSupported(), addbaRequest->getBlockAckPolicy() == 0);
17+
ASSERT(addbaRequest->getDialogToken() != 0);
18+
OriginatorBlockAckAgreement *blockAckAgreement = new OriginatorBlockAckAgreement(addbaRequest->getReceiverAddress(), addbaRequest->getTid(), addbaRequest->getStartingSequenceNumber(), addbaRequest->getBufferSize(), addbaRequest->getAMsduSupported(), addbaRequest->getBlockAckPolicy() == 0, addbaRequest->getDialogToken());
1819
auto agreementId = std::make_pair(addbaRequest->getReceiverAddress(), addbaRequest->getTid());
1920
blockAckAgreements[agreementId] = blockAckAgreement;
2021
}
2122

23+
uint8_t OriginatorBlockAckAgreementHandler::allocateDialogToken()
24+
{
25+
auto dialogToken = nextDialogToken;
26+
nextDialogToken = nextDialogToken == 255 ? 1 : nextDialogToken + 1;
27+
return dialogToken;
28+
}
29+
2230
simtime_t OriginatorBlockAckAgreementHandler::computeEarliestExpirationTime()
2331
{
2432
simtime_t earliestTime = SIMTIME_MAX;
@@ -33,6 +41,35 @@ simtime_t OriginatorBlockAckAgreementHandler::computeEarliestExpirationTime()
3341
return earliestTime;
3442
}
3543

44+
simtime_t OriginatorBlockAckAgreementHandler::computeEarliestAddbaResponseDeadline() const
45+
{
46+
simtime_t earliestDeadline = SIMTIME_MAX;
47+
for (const auto& entry : blockAckAgreements) {
48+
auto agreement = entry.second;
49+
if (agreement->isPending() && agreement->getIsAddbaRequestSent()) {
50+
ASSERT(agreement->getAddbaResponseDeadline() >= 0);
51+
earliestDeadline = std::min(earliestDeadline, agreement->getAddbaResponseDeadline());
52+
}
53+
}
54+
return earliestDeadline;
55+
}
56+
57+
void OriginatorBlockAckAgreementHandler::addbaResponseTimeoutExpired(IBlockAckAgreementHandlerCallback *callback)
58+
{
59+
auto now = simTime();
60+
for (auto it = blockAckAgreements.begin(); it != blockAckAgreements.end();) {
61+
auto agreement = it->second;
62+
if (agreement->isPending() && agreement->getIsAddbaRequestSent() && agreement->getAddbaResponseDeadline() <= now) {
63+
EV_INFO << "ADDBA Response timeout for receiver=" << agreement->getReceiverAddr() << " tid=" << (int)agreement->getTid() << endl;
64+
it = blockAckAgreements.erase(it);
65+
delete agreement;
66+
}
67+
else
68+
it++;
69+
}
70+
scheduleAddbaResponseTimer(callback);
71+
}
72+
3673
void OriginatorBlockAckAgreementHandler::blockAckAgreementExpired(IProcedureCallback *procedureCallback, IBlockAckAgreementHandlerCallback *agreementHandlerCallback)
3774
{
3875
// When a timeout of BlockAckTimeout is detected, the STA shall send a DELBA frame to the
@@ -57,6 +94,9 @@ const Ptr<Ieee80211AddbaRequest> OriginatorBlockAckAgreementHandler::buildAddbaR
5794
{
5895
auto addbaRequest = makeShared<Ieee80211AddbaRequest>();
5996
addbaRequest->setReceiverAddress(receiverAddr);
97+
// IEEE Std 802.11-2024, 9.6.4.2: a solicited ADDBA Request uses a nonzero
98+
// Dialog Token, and the corresponding response copies that token.
99+
addbaRequest->setDialogToken(allocateDialogToken());
60100
addbaRequest->setTid(tid);
61101
addbaRequest->setAMsduSupported(blockAckAgreementPolicy->isMsduSupported());
62102
addbaRequest->setBlockAckTimeoutValue(blockAckAgreementPolicy->getBlockAckTimeoutValue());
@@ -92,13 +132,24 @@ void OriginatorBlockAckAgreementHandler::scheduleInactivityTimer(IBlockAckAgreem
92132
callback->scheduleInactivityTimer(earliestExpirationTime);
93133
}
94134

135+
void OriginatorBlockAckAgreementHandler::scheduleAddbaResponseTimer(IBlockAckAgreementHandlerCallback *callback)
136+
{
137+
callback->scheduleAddbaResponseTimer(computeEarliestAddbaResponseDeadline());
138+
}
139+
95140
OriginatorBlockAckAgreement *OriginatorBlockAckAgreementHandler::getAgreement(MacAddress receiverAddr, Tid tid)
96141
{
97142
auto agreementId = std::make_pair(receiverAddr, tid);
98143
auto it = blockAckAgreements.find(agreementId);
99144
return it != blockAckAgreements.end() ? it->second : nullptr;
100145
}
101146

147+
bool OriginatorBlockAckAgreementHandler::isAddbaResponsePending(MacAddress receiverAddr, Tid tid) const
148+
{
149+
auto it = blockAckAgreements.find(std::make_pair(receiverAddr, tid));
150+
return it != blockAckAgreements.end() && it->second->isPending();
151+
}
152+
102153
const Ptr<Ieee80211Delba> OriginatorBlockAckAgreementHandler::buildDelba(MacAddress receiverAddr, Tid tid, int reasonCode)
103154
{
104155
auto delba = makeShared<Ieee80211Delba>();
@@ -132,15 +183,23 @@ void OriginatorBlockAckAgreementHandler::processTransmittedDataFrame(Packet *pac
132183
}
133184
}
134185

135-
void OriginatorBlockAckAgreementHandler::processReceivedAddbaResp(const Ptr<const Ieee80211AddbaResponse>& addbaResp, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback)
186+
OriginatorBlockAckAgreement *OriginatorBlockAckAgreementHandler::processReceivedAddbaResp(const Ptr<const Ieee80211AddbaResponse>& addbaResp, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback)
136187
{
137188
auto agreement = getAgreement(addbaResp->getTransmitterAddress(), addbaResp->getTid());
138-
if (blockAckAgreementPolicy->isAddbaReqAccepted(addbaResp, agreement)) {
189+
// IEEE Std 802.11-2024, 11.5.2.2: only a successful response matching the
190+
// outstanding peer, TID, and Dialog Token establishes the agreement.
191+
if (agreement == nullptr || !agreement->isPending() || !agreement->getIsAddbaRequestSent() || agreement->getDialogToken() != addbaResp->getDialogToken())
192+
return nullptr;
193+
if (addbaResp->getStatusCode() == 0 && blockAckAgreementPolicy->isAddbaReqAccepted(addbaResp, agreement)) {
139194
updateAgreement(agreement, addbaResp);
140195
scheduleInactivityTimer(callback);
196+
scheduleAddbaResponseTimer(callback);
197+
return agreement;
141198
}
142199
else {
143-
// TODO send a new one?
200+
terminateAgreement(addbaResp->getTransmitterAddress(), addbaResp->getTid());
201+
scheduleAddbaResponseTimer(callback);
202+
return nullptr;
144203
}
145204
}
146205

@@ -152,13 +211,21 @@ void OriginatorBlockAckAgreementHandler::updateAgreement(OriginatorBlockAckAgree
152211
agreement->calculateExpirationTime();
153212
}
154213

155-
void OriginatorBlockAckAgreementHandler::processTransmittedAddbaReq(const Ptr<const Ieee80211AddbaRequest>& addbaReq)
214+
void OriginatorBlockAckAgreementHandler::processTransmittedAddbaReq(const Ptr<const Ieee80211AddbaRequest>& addbaReq, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback)
156215
{
157216
auto agreement = getAgreement(addbaReq->getReceiverAddress(), addbaReq->getTid());
158-
if (agreement)
217+
if (agreement && agreement->isPending() && agreement->getDialogToken() == addbaReq->getDialogToken()) {
218+
if (agreement->getAddbaResponseDeadline() < 0) {
219+
auto addbaFailureTimeout = blockAckAgreementPolicy->computeAddbaFailureTimeout();
220+
if (addbaFailureTimeout <= 0)
221+
throw cRuntimeError("ADDBA failure timeout must be greater than zero");
222+
agreement->setAddbaResponseDeadline(simTime() + addbaFailureTimeout);
223+
}
159224
agreement->setIsAddbaRequestSent(true);
225+
scheduleAddbaResponseTimer(callback);
226+
}
160227
else
161-
throw cRuntimeError("Block Ack Agreement should have already been added");
228+
EV_WARN << "Ignoring stale transmitted ADDBA Request for receiver=" << addbaReq->getReceiverAddress() << " tid=" << (int)addbaReq->getTid() << " dialogToken=" << (int)addbaReq->getDialogToken() << endl;
162229
}
163230

164231
void OriginatorBlockAckAgreementHandler::processTransmittedDelba(const Ptr<const Ieee80211Delba>& delba)
@@ -180,4 +247,3 @@ OriginatorBlockAckAgreementHandler::~OriginatorBlockAckAgreementHandler()
180247

181248
} // namespace ieee80211
182249
} // namespace inet
183-

src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,31 +21,36 @@ class INET_API OriginatorBlockAckAgreementHandler : public IOriginatorBlockAckAg
2121
{
2222
protected:
2323
std::map<std::pair<MacAddress, Tid>, OriginatorBlockAckAgreement *> blockAckAgreements;
24+
uint8_t nextDialogToken = 1;
2425

2526
protected:
2627
virtual const Ptr<Ieee80211AddbaRequest> buildAddbaRequest(MacAddress receiverAddr, Tid tid, SequenceNumberCyclic startingSequenceNumber, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy);
28+
virtual uint8_t allocateDialogToken();
2729
virtual void createAgreement(const Ptr<const Ieee80211AddbaRequest>& addbaRequest);
2830
virtual void updateAgreement(OriginatorBlockAckAgreement *agreement, const Ptr<const Ieee80211AddbaResponse>& addbaResp);
2931
virtual void terminateAgreement(MacAddress originatorAddr, Tid tid);
3032
virtual const Ptr<Ieee80211Delba> buildDelba(MacAddress receiverAddr, Tid tid, int reasonCode);
3133
virtual simtime_t computeEarliestExpirationTime();
34+
virtual simtime_t computeEarliestAddbaResponseDeadline() const;
3235
virtual void scheduleInactivityTimer(IBlockAckAgreementHandlerCallback *callback);
36+
virtual void scheduleAddbaResponseTimer(IBlockAckAgreementHandlerCallback *callback);
3337

3438
public:
3539
virtual ~OriginatorBlockAckAgreementHandler();
36-
virtual void processTransmittedAddbaReq(const Ptr<const Ieee80211AddbaRequest>& addbaReq) override;
40+
virtual void processTransmittedAddbaReq(const Ptr<const Ieee80211AddbaRequest>& addbaReq, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback) override;
3741
virtual void processTransmittedDataFrame(Packet *packet, const Ptr<const Ieee80211DataHeader>& dataHeader, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IProcedureCallback *callback) override;
3842
virtual void processReceivedBlockAck(const Ptr<const Ieee80211BlockAck>& blockAck, IBlockAckAgreementHandlerCallback *callback) override;
39-
virtual void processReceivedAddbaResp(const Ptr<const Ieee80211AddbaResponse>& addbaResp, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback) override;
43+
virtual OriginatorBlockAckAgreement *processReceivedAddbaResp(const Ptr<const Ieee80211AddbaResponse>& addbaResp, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy, IBlockAckAgreementHandlerCallback *callback) override;
4044
virtual void processReceivedDelba(const Ptr<const Ieee80211Delba>& delba, IOriginatorBlockAckAgreementPolicy *blockAckAgreementPolicy) override;
4145
virtual void processTransmittedDelba(const Ptr<const Ieee80211Delba>& delba) override;
4246
virtual void blockAckAgreementExpired(IProcedureCallback *procedureCallback, IBlockAckAgreementHandlerCallback *agreementHandlerCallback) override;
47+
virtual void addbaResponseTimeoutExpired(IBlockAckAgreementHandlerCallback *callback) override;
4348

4449
virtual OriginatorBlockAckAgreement *getAgreement(MacAddress receiverAddr, Tid tid) override;
50+
virtual bool isAddbaResponsePending(MacAddress receiverAddr, Tid tid) const override;
4551
};
4652

4753
} // namespace ieee80211
4854
} // namespace inet
4955

5056
#endif
51-

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@ void OriginatorBlockAckAgreementPolicy::initialize(int stage)
2424
aMsduSupported = par("aMsduSupported");
2525
maximumAllowedBufferSize = par("maximumAllowedBufferSize");
2626
blockAckTimeoutValue = par("blockAckTimeoutValue");
27-
// TODO addbaFailureTimeout = par("addbaFailureTimeout");
27+
addbaFailureTimeout = par("addbaFailureTimeout");
28+
if (addbaFailureTimeout <= 0)
29+
throw cRuntimeError("addbaFailureTimeout must be greater than zero");
2830
WATCH(blockAckReqThreshold);
2931
}
3032
}
3133

3234
simtime_t OriginatorBlockAckAgreementPolicy::computeAddbaFailureTimeout() const
3335
{
34-
// TODO ADDBAFailureTimeout -- 6.3.29.2.2 Semantics of the service primitive
35-
throw cRuntimeError("Unimplemented");
36+
return addbaFailureTimeout;
3637
}
3738

3839
bool OriginatorBlockAckAgreementPolicy::isAddbaReqNeeded(Packet *packet, const Ptr<const Ieee80211DataHeader>& header)
@@ -42,8 +43,7 @@ bool OriginatorBlockAckAgreementPolicy::isAddbaReqNeeded(Packet *packet, const P
4243

4344
bool OriginatorBlockAckAgreementPolicy::isAddbaReqAccepted(const Ptr<const Ieee80211AddbaResponse>& addbaResp, OriginatorBlockAckAgreement *agreement)
4445
{
45-
ASSERT(agreement);
46-
return true;
46+
return agreement != nullptr && addbaResp->getStatusCode() == 0;
4747
}
4848

4949
bool OriginatorBlockAckAgreementPolicy::isDelbaAccepted(const Ptr<const Ieee80211Delba>& delba)
@@ -53,4 +53,3 @@ bool OriginatorBlockAckAgreementPolicy::isDelbaAccepted(const Ptr<const Ieee8021
5353

5454
} /* namespace ieee80211 */
5555
} /* namespace inet */
56-

src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.ned

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ simple OriginatorBlockAckAgreementPolicy extends SimpleModule like IOriginatorBl
2323
bool aMsduSupported = default(true);
2424
int maximumAllowedBufferSize = default(64);
2525
double blockAckTimeoutValue @unit(s) = default(0s); // 0 means that it depends on the originator
26+
double addbaFailureTimeout @unit(s) = default(1s);
2627
@display("i=block/control");
2728
}
28-

0 commit comments

Comments
 (0)