Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
ff486eb
queueing: support selective extraction and departure signals
mgonzalezlopezudc Sep 9, 2026
cdd5026
ieee80211: track pending queue departures through signals
mgonzalezlopezudc Sep 9, 2026
e4f08f3
ieee80211: make originator ADDBA transactions explicit
mgonzalezlopezudc Sep 9, 2026
9677307
ieee80211: reset recipient Block Ack state on renegotiation
mgonzalezlopezudc Sep 9, 2026
32b0dd7
ieee80211: reassemble fragmented action frames before dispatch
mgonzalezlopezudc Sep 9, 2026
74283b1
ieee80211: cancel superseded management transactions
mgonzalezlopezudc Sep 9, 2026
d9ba185
ieee80211: protect Block Ack teardown generations
mgonzalezlopezudc Sep 9, 2026
3d72fef
ieee80211: schedule Block Ack inactivity with absolute deadlines
mgonzalezlopezudc Sep 9, 2026
d8bc79b
ieee80211: make fragment reassembly generation-safe
mgonzalezlopezudc Sep 9, 2026
5e0f871
ieee80211: enforce receive lifetime during Block Ack reordering
mgonzalezlopezudc Sep 9, 2026
cfe1c02
ieee80211: quarantine expired Block Ack agreements
mgonzalezlopezudc Sep 9, 2026
8dbee7b
ieee80211: fix recipient BAR and ADDBA timeout handling
mgonzalezlopezudc Sep 9, 2026
47e01bf
ieee80211: enforce valid A-MSDU fragmentation and sizing
mgonzalezlopezudc Sep 9, 2026
43a47ef
tests: record wireless fingerprints for the ADDBA rework
mgonzalezlopezudc Sep 9, 2026
c632762
ieee80211: preserve management bodies during fragmentation
mgonzalezlopezudc Sep 9, 2026
88eac86
ieee80211: flag malformed peer association IDs without throwing
mgonzalezlopezudc Sep 9, 2026
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
71 changes: 71 additions & 0 deletions WHATSNEW
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,77 @@ Notable backward incompatible changes are the following:
adaptive rate control, either where a station transmits to several peers or
where it sends group-addressed traffic.

2. Packet queue extraction and lifecycle signals

IPacketQueue now supports queue-lifecycle signals and predicate-based extraction.
Direct C++ implementations of IPacketQueue must implement findPacket(), the
predicate overload of dequeuePacket(), and must emit packetQueueDeparture
with PacketQueueRemovalDetails exactly once whenever a packet is dequeued,
explicitly removed, or destructively dropped. Listeners subscribe to the queue
module and filter the source to exclude descendant queue emissions. Providers
connected to CompoundPacketQueueBase must also implement IPacketExtractor so
that a compound queue preserves the provider's scheduling policy when it
extracts a matching packet. Direct C++
implementations of IPacketExtractor must replace selected-pointer extraction
with findPacket(predicate) and dequeuePacket(predicate); predicates may be
evaluated repeatedly and must be stable and side-effect free throughout one
logical selection. WrrScheduler, LabelScheduler, and PriorityScheduler accept
ordinary IPassivePacketSource inputs; they require IPacketCollection only when
collection/aggregate access is used and IPacketExtractor only when predicate
extraction is used, reporting the unsupported operation lazily.
PriorityScheduler aggregate queries no longer return -1 when an input lacks
IPacketCollection; getNumPackets() and getTotalLength() now report that
unsupported operation with cRuntimeError. Update callers that treated -1 as
an unknown aggregate size, or connect collection-capable providers.
A-MSDU policies now receive the provider-selected anchor and frame-eligibility
predicate. Additional policy-selected members are removed through exact
predicate dequeues, preserving scheduler/flow accounting without requiring
collection enumeration order to match scheduling order.
BasicMsduAggregationPolicy conservatively considers only collection members
after the anchor and never bypasses an earlier blocked same-flow member.
IPacketBuffer::ICallback also provides an optional handlePacketDropped()
notification. Shared buffers invoke it only after all victims selected by one
overload operation have been detached from their owning queues. Compound
queues propagate destructive drops through arbitrary non-queue wrappers and
nested compound queues exactly once. PacketBuffer rejects packets owned by a
cPacketQueue whose owner cannot participate in the buffer callback contract.

3. Block Ack DELBA agreement ownership

IOriginatorBlockAckAgreementHandler::processReceivedDelba(),
IOriginatorBlockAckAgreementHandler::processTransmittedDelba(), and
IRecipientBlockAckAgreementHandler::processReceivedDelba() now return the
terminated agreement as a unique_ptr. Originator ADDBA response processing
now returns a typed outcome containing the established agreement and, when
local policy vetoes a successful response, its immediately terminated local
agreement and a best-effort initiator DELBA for Hcf to enqueue after emitting
the Added and Deleted signals. Such locally generated DELBAs carry
sender-local agreement-generation metadata (the originator reuses its ADDBA
transaction identity), and ordinary data continues with Normal Ack. They remain
eligible through the
final fragment; aborting one fragment cancels its siblings. After retry backoff,
a replacement ADDBA setup invalidates an older queued DELBA, so an unreported
disposal cannot suppress setup indefinitely and a delayed frame cannot
terminate a newer peer/TID agreement. The replacement setup reports that
obsolete identity so Hcf also removes all of its queued or in-progress packets.
Originator and recipient DELBA transmission handlers now receive the full
Packet so sender-local agreement-generation metadata is retained through
fragmentation and MAC retries. This changes
IRecipientBlockAckAgreementHandler::processTransmittedDelba() from accepting
only a DELBA header to accepting Packet*; custom handler implementations and
callers must migrate to the Packet form. A locally tagged DELBA carrying
agreement-generation metadata remains eligible across MAC retries and is
retired only when its final fragment is acknowledged or the teardown is
terminally aborted. Custom handler
implementations and callers must adopt processAcknowledgedDelba() and the
boolean processAbortedDelba() outcome; processTransmittedDelba() still returns
the agreement removed by an untagged DELBA, or null when none was removed.

OriginatorBlockAckAgreementPolicy now exposes the new `addbaResponseTimeout`
and `addbaRetryBackoff` NED parameters. C++ implementations of
IOriginatorBlockAckAgreementPolicy must replace computeAddbaFailureTimeout()
with getAddbaResponseTimeout() and implement computeAddbaRetryBackoff().

Notable backward compatible changes are the following:

1. IEEE 802.11 per-station rate statistics
Expand Down
9 changes: 5 additions & 4 deletions doc/src/users-guide/ch-diffserv.rst
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,11 @@ interface is ready to transmit one. They have several input gates and
one output gate.

Modules that are connected to the inputs of a scheduler must implement
the :cpp:`IPacketQueue` C++ interface. Schedulers also implement the
:cpp:`IPacketQueue` interface, so they can be cascaded to other
schedulers and used as the output module of :ned:`IPacketQueue`'s.
the :cpp:`IPassivePacketSource` C++ interface. Collection and predicate
extraction operations additionally require the corresponding input provider
to implement :cpp:`IPacketCollection` and :cpp:`IPacketExtractor`, respectively.
Schedulers can be cascaded and used as the output module of compound packet
queues when those additional interfaces are available.

There are several possible scheduling disciplines (first come/first
served, priority, weighted fair, weighted round-robin, deadline-based,
Expand Down Expand Up @@ -589,4 +591,3 @@ implement three different drop priorities within the class. BE packets are
stored in a drop tail queue. Packets from AFxy and BE queues are
scheduled by a WRR scheduler, which ensures that the remaining bandwidth
is allocated among the classes according to the specified weights.

23 changes: 23 additions & 0 deletions examples/wireless/qos/omnetpp.ini
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,26 @@ extends = MacQos

# radio medium
*.radioMedium.sameTransmissionStartTimeCheck = "ignore"

[Config MacQosWithTransactionalBlockAck]
description = "Exercises successful and timed-out ADDBA transactions"
extends = MacQosWithoutAggregation
sim-time-limit = 3s

# Use one voice flow so each wireless hop has a single peer/TID data flow.
*.cliHost.numApps = 1
*.cliHost.app[0].destPort = 5000
*.cliHost.app[0].packetName = "TransactionalBlockAck"
*.cliHost.app[0].startTime = 1s
*.cliHost.app[0].stopTime = 2s
*.cliHost.app[0].sendInterval = 10ms
*.srvHost.numApps = 1
*.srvHost.app[0].localPort = 5000

# The client-to-AP transaction succeeds. The AP-to-server transaction times
# out, because the server deliberately does not support Block Ack.
*.cliHost.wlan[0].mac.hcf.isBlockAckSupported = true
*.ap.wlan[0].mac.hcf.isBlockAckSupported = true
*.srvHost.wlan[0].mac.hcf.isBlockAckSupported = false
**.mac.hcf.originatorAckPolicy.blockAckReqThreshold = 2
**.mac.hcf.originatorBlockAckAgreementPolicy.addbaResponseTimeout = 250ms
2 changes: 1 addition & 1 deletion src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class Ieee80211AddbaResponse extends Ieee80211ActionFrame
//
class Ieee80211Delba extends Ieee80211ActionFrame
{
chunkLength = LENGTH_DELBA;
chunkLength = LENGTH_DELBA - B(4);
category = 3; // Category field is set to 3 (representing DELBA). (1 byte)
blockAckAction = 2; // Block Ack Action field is set to 2 (representing DELBA). (1 byte)

Expand Down
9 changes: 9 additions & 0 deletions src/inet/linklayer/ieee80211/mac/Ieee80211Mac.cc
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,15 @@ void Ieee80211Mac::sendDownPendingRadioConfigMsg()
}
}

void Ieee80211Mac::cancelManagementTransaction(uint64_t transactionId)
{
Enter_Method("cancelManagementTransaction");
if (mib->qos)
hcf->cancelManagementTransaction(transactionId);
else
dcf->cancelManagementTransaction(transactionId);
}

void Ieee80211Mac::processUpperFrame(Packet *packet, const Ptr<const Ieee80211DataOrMgmtHeader>& header)
{
Enter_Method("processUpperFrame(\"%s\")", packet->getName());
Expand Down
5 changes: 4 additions & 1 deletion src/inet/linklayer/ieee80211/mac/Ieee80211Mac.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "inet/linklayer/ieee80211/mac/contract/IRateSelection.h"
#include "inet/linklayer/ieee80211/mac/contract/IRx.h"
#include "inet/linklayer/ieee80211/mac/contract/ITx.h"
#include "inet/linklayer/ieee80211/mac/contract/IManagementFrameTransactionHandler.h"
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.h"
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.h"
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Mcf.h"
Expand All @@ -35,7 +36,7 @@ class Ieee80211MacHeader;
* exact operation of the MAC depend on the plugged-in components (see IUpperMac,
* IRx, ITx, IContention and other interface classes).
*/
class INET_API Ieee80211Mac : public MacProtocolBase
class INET_API Ieee80211Mac : public MacProtocolBase, public IManagementFrameTransactionHandler
{
public:
static simsignal_t frameTransmissionOutcomeSignal;
Expand Down Expand Up @@ -107,6 +108,8 @@ class INET_API Ieee80211Mac : public MacProtocolBase
virtual void sendDownFrame(Packet *frame);
virtual void sendDownPendingRadioConfigMsg();

virtual void cancelManagementTransaction(uint64_t transactionId) override;

virtual void processUpperFrame(Packet *packet, const Ptr<const Ieee80211DataOrMgmtHeader>& header);
virtual void processLowerFrame(Packet *packet, const Ptr<const Ieee80211MacHeader>& header);
};
Expand Down
16 changes: 16 additions & 0 deletions src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
stream.writeUint32Be(0);
if (type == ST_ACTION) {
auto actionFrame = dynamicPtrCast<const Ieee80211ActionFrame>(chunk);
// A fragmented action MPDU uses a generic management header;
// its action-body slice is a separate packet chunk.
if (actionFrame == nullptr)
break;
switch (actionFrame->getCategory()) {
case 3: {
stream.writeByte(actionFrame->getCategory());
Expand Down Expand Up @@ -423,6 +427,17 @@ const Ptr<Chunk> Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
actionFrame->setSequenceNumber(sequenceNumber);
if (order)
stream.readUint32Be();
if (actionFrame->getMoreFragments() || actionFrame->getFragmentNumber() != 0) {
auto mgmtHeader = makeShared<Ieee80211MgmtHeader>();
copyBasicFields(mgmtHeader, macHeader);
mgmtHeader->setDurationField(actionFrame->getDurationField());
mgmtHeader->setReceiverAddress(actionFrame->getReceiverAddress());
mgmtHeader->setTransmitterAddress(actionFrame->getTransmitterAddress());
mgmtHeader->setAddress3(actionFrame->getAddress3());
mgmtHeader->setFragmentNumber(actionFrame->getFragmentNumber());
mgmtHeader->setSequenceNumber(actionFrame->getSequenceNumber());
return mgmtHeader;
}
actionFrame->setCategory(stream.readByte());
switch (actionFrame->getCategory()) {
case 3: {
Expand Down Expand Up @@ -460,6 +475,7 @@ const Ptr<Chunk> Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre
case 2: {
auto delba = makeShared<Ieee80211Delba>();
copyBasicFields(delba, macHeader);
copyActionFrameFields(delba, actionFrame);
delba->setBlockAckAction(blockAckAction);
delba->setReserved(stream.readNBitsToUint64Be(11));
delba->setInitiator(stream.readBit());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,14 @@ void Ieee80211MacProtocolDissector::dissect(Packet *packet, const Protocol *prot
else
callback.dissectPacket(packet, computeLlcProtocol(packet));
}
else if (dynamicPtrCast<const inet::ieee80211::Ieee80211ActionFrame>(header))
ASSERT(packet->getDataLength() == b(0));
else if (dynamicPtrCast<const inet::ieee80211::Ieee80211MgmtHeader>(header))
callback.dissectPacket(packet, &Protocol::ieee80211Mgmt);
else if (auto mgmtHeader = dynamicPtrCast<const inet::ieee80211::Ieee80211MgmtHeader>(header)) {
if (mgmtHeader->getMoreFragments() || mgmtHeader->getFragmentNumber() != 0)
callback.dissectPacket(packet, nullptr);
else if (dynamicPtrCast<const inet::ieee80211::Ieee80211ActionFrame>(header))
ASSERT(packet->getDataLength() == b(0));
else
callback.dissectPacket(packet, &Protocol::ieee80211Mgmt);
}
// TODO else if (dynamicPtrCast<const inet::ieee80211::Ieee80211ControlFrame>(header))
else
ASSERT(packet->getDataLength() == b(0));
Expand All @@ -77,4 +81,3 @@ void Ieee80211MacProtocolDissector::dissect(Packet *packet, const Protocol *prot
}

} // namespace inet

13 changes: 12 additions & 1 deletion src/inet/linklayer/ieee80211/mac/Tx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ void Tx::transmitFrame(Packet *packet, const Ptr<const Ieee80211MacHeader>& head
scheduleAfter(ifs, endIfsTimer);
}

bool Tx::cancelPendingTransmission(ITx::ICallback *owner)
{
Enter_Method("cancelPendingTransmission");
if (this->txCallback != owner || transmitting || frame == nullptr || !endIfsTimer->isScheduled())
return false;
cancelEvent(endIfsTimer);
delete frame;
frame = nullptr;
txCallback = nullptr;
return true;
}

void Tx::radioTransmissionFinished()
{
Enter_Method("radioTransmissionFinished");
Expand Down Expand Up @@ -114,4 +126,3 @@ void Tx::handleMessage(cMessage *msg)

} // namespace ieee80211
} // namespace inet

2 changes: 1 addition & 1 deletion src/inet/linklayer/ieee80211/mac/Tx.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ class INET_API Tx : public SimpleModule, public ITx

virtual void transmitFrame(Packet *packet, const Ptr<const Ieee80211MacHeader>& header, ITx::ICallback *txCallback) override;
virtual void transmitFrame(Packet *packet, const Ptr<const Ieee80211MacHeader>& header, simtime_t ifs, ITx::ICallback *txCallback) override;
virtual bool cancelPendingTransmission(ITx::ICallback *owner) override;
virtual void radioTransmissionFinished() override;
};

} // namespace ieee80211
} // namespace inet

#endif

Loading
Loading