Skip to content

Fix IEEE 802.11 ADDBA transaction handling - #1129

Open
mgonzalezlopezudc wants to merge 17 commits into
inet-framework:masterfrom
mgonzalezlopezudc:fix-ieee80211-addba-transaction
Open

Fix IEEE 802.11 ADDBA transaction handling#1129
mgonzalezlopezudc wants to merge 17 commits into
inet-framework:masterfrom
mgonzalezlopezudc:fix-ieee80211-addba-transaction

Conversation

@mgonzalezlopezudc

Copy link
Copy Markdown
Contributor

Summary

  • model solicited ADDBA setup as an explicit pending transaction identified by peer, TID, nonzero dialog token, and negotiated starting sequence number
  • accept only matching successful responses, send explicit acceptance or rejection responses, and recover pending transactions with a configurable response timeout
  • stage recipient agreements until the exact response packet is transmitted, preserving overlapping and renegotiated transactions across header copy-on-write
  • hold same-peer/TID QoS frames while setup is pending so sequence assignment cannot consume the negotiated SSN, then resume every eligible EDCA queue after success, rejection, or timeout
  • add focused unit coverage and a deterministic MacQosWithTransactionalBlockAck example/fingerprint exercising successful and timed-out transactions

Why

The previous implementation did not correlate ADDBA responses with an outstanding dialog token, treated policy acceptance as sufficient regardless of status, did not implement response-timeout recovery, and created recipient agreement state before the successful response was transmitted. Meanwhile, queued frames could receive sequence numbers while setup was pending, so the first frame sent under an accepted agreement could diverge from the SSN carried by the request.

This change follows IEEE Std 802.11-2024 clauses 9.6.4.2, 10.25.2, 10.25.6.6.1, 11.5.2.2, and 11.5.2.3. It is based directly on master and has no dependency on Compressed Block Ack support.

Validation

  • make MODE=release -j$(nproc)
  • make MODE=debug -j$(nproc)
  • inet_run_unit_tests -m release -f '(Ieee80211AddbaTransaction_1|Ieee80211OnWireBitCompliance_1)\.test' — 2/2 passed
  • ./fingerprinttest -d -m '/examples/wireless/qos/.*MacQosWithTransactionalBlockAck' -f 'tplx' -f '~tNl' -f '~tND' — 1/1 passed
  • git diff --check

The complete release unit run passed all ADDBA/IEEE 802.11 tests; its 12 unexpected failures were confined to unrelated TCP receive-queue, clock, and oscillator tests. The complete fingerprint run identifies intentional trajectory changes in the pre-existing MacQosWithBlockAck case and three wireless Block Ack showcase cases; their expected rows are deliberately not updated in this draft.

@mgonzalezlopezudc
mgonzalezlopezudc marked this pull request as ready for review August 16, 2026 18:32

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Comment on lines +86 to +89
[Config MacQosWithTransactionalBlockAck]
description = "Exercises successful and timed-out ADDBA transactions"
extends = MacQosWithoutAggregation
sim-time-limit = 3s

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Existing Block Ack example loses its radio medium setting and can abort with an error

The new example configuration is inserted (at examples/wireless/qos/omnetpp.ini:86) between the previous last configuration and the trailing radio-medium setting lines, so the setting that used to belong to the Block Ack example silently moves to the new example.
Impact: The pre-existing Block Ack example now runs with the strict default and can stop with a runtime error when two transmissions start at the same moment; its recorded results change too.

How the ini section boundary shifts the setting

In an omnetpp.ini file, keys belong to the section they textually follow. Before this PR, the lines

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

were the last lines of [Config MacQosWithBlockAck]. The new [Config MacQosWithTransactionalBlockAck] block is inserted above them, so those two lines now belong to the new config and MacQosWithBlockAck falls back to the NED default error (src/inet/physicallayer/wireless/common/medium/RadioMedium.ned:44), which raises a runtime error via src/inet/physicallayer/wireless/common/medium/RadioMedium.cc:484-497. Note also that MacQosWithTransactionalBlockAck extends MacQosWithoutAggregation, so the new config would not have inherited the setting either; this reassignment is accidental. The existing fingerprint row for MacQosWithBlockAck in tests/fingerprint/examples.csv:661 is left unchanged.

Prompt for agents
The two trailing lines of examples/wireless/qos/omnetpp.ini ('# radio medium' and '*.radioMedium.sameTransmissionStartTimeCheck = "ignore"') were part of [Config MacQosWithBlockAck] because ini keys belong to the preceding section. The newly added [Config MacQosWithTransactionalBlockAck] section was inserted before them, so MacQosWithBlockAck lost the setting and now uses the RadioMedium default 'error', which can abort the simulation. Restore the setting to MacQosWithBlockAck (e.g. append the new config after those lines, or explicitly duplicate/hoist the radioMedium assignment where it is actually needed).
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Comment on lines +227 to +231
if (!hasFrameToTransmit(ac)) {
EV_DETAIL << "Releasing channel because no eligible frame is available.\n";
edcaf->releaseChannel(this);
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Traffic queues can stall after the radio is handed back without sending anything

When the radio is granted to a queue that currently has nothing sendable, the radio is handed back immediately (edcaf->releaseChannel(this) at src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc:229) before the other queues that lost the simultaneous grant are told to back off and retry, so those queues stop contending.
Impact: Packets waiting in other priority queues can sit unsent until unrelated new traffic arrives, causing extra delay or loss.

Skipped internal-collision recovery

When several access categories reach their transmit time simultaneously, only the highest-priority EDCAF gets channelGranted(); the losers are marked as internal collisions (src/inet/linklayer/ieee80211/mac/contention/EdcaCollisionController.cc:33-46) and do not get their callback (src/inet/linklayer/ieee80211/mac/channelaccess/Edcaf.cc:127-133). Their recovery (backoff update, drop on retry limit, and edcaf->requestChannel(this)) is performed exclusively by the winner in Hcf::handleInternalCollision() (src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc:229-297 region). The new early return at line 230 executes before edca->getInternallyCollidedEdcafs() is consulted, so the collided EDCAFs' contention has already ended and nobody restarts it. The early return is reachable because frame eligibility can change between requesting the channel and being granted it (a pending ADDBA transaction makes all same peer/TID QoS frames ineligible via Hcf::hasFrameToTransmit(AccessCategory)).

Prompt for agents
In Hcf::channelGranted(), the new early return releases the channel when no eligible frame exists, but it bypasses the internal-collision handling that follows (edca->getInternallyCollidedEdcafs() / handleInternalCollision()). EDCAFs that lost the simultaneous grant rely on the winner to run their recovery procedure and re-request the channel, so they stop contending. Handle the internally collided EDCAFs (and emit edcaCollisionDetectedSignal) before releasing the channel and returning.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked as Outdated by Devin

Comment on lines 199 to 203
else {
// TODO send a new one?
terminateAgreement(addbaResp->getTransmitterAddress(), addbaResp->getTid());
scheduleAddbaResponseTimer(callback);
return nullptr;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Traffic to a peer that refuses Block Ack is throttled to one frame per timeout period

The Block Ack setup record is deleted (terminateAgreement(...) at src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc:200, and by the timeout cleanup at lines 60-69) when the peer refuses or never answers, so the very next data frame starts a brand new setup attempt that again blocks all further frames for that peer and priority.
Impact: Data sent to a receiver that does not support Block Ack is repeatedly held for the whole setup timeout, collapsing throughput and overflowing the queue.

Retry loop between eligibility gating and agreement deletion

While a setup is pending, Hcf's frame-eligibility function (src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc:63-66) makes every QoS data frame to that receiver/TID ineligible, so they stay queued. On rejection (processReceivedAddbaResp) or on timeout (addbaResponseTimeoutExpired) the agreement object is erased entirely. processTransmittedDataFrame (src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc:176-183) creates a new agreement whenever getAgreement() returns null and isAddbaReqNeeded() is true; the default policy's isAddbaReqNeeded() only checks frame type and length (src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementPolicy.cc:38-41), so it is true for every QoS data frame. The result is: one frame goes out, a new ADDBA is issued, all following frames are held for addbaFailureTimeout (default 1s), the setup fails again, and the cycle repeats indefinitely. Previously the pending agreement was kept forever, so only one ADDBA was ever sent and data flowed normally with normal ACK. Consider remembering failed peers/TIDs (or a retry limit/backoff) so ADDBA is not re-attempted for every frame, and/or not gating frames when no agreement can be established.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asked Devin again, his answer: "This flag no longer applies. The PR added exactly the retry-backoff mechanism the flag recommends."

Comment on lines +217 to +226
if (agreement && agreement->isPending() && agreement->getDialogToken() == addbaReq->getDialogToken()) {
if (agreement->getAddbaResponseDeadline() < 0) {
auto addbaFailureTimeout = blockAckAgreementPolicy->computeAddbaFailureTimeout();
if (addbaFailureTimeout <= 0)
throw cRuntimeError("ADDBA failure timeout must be greater than zero");
agreement->setAddbaResponseDeadline(simTime() + addbaFailureTimeout);
}
agreement->setIsAddbaRequestSent(true);
scheduleAddbaResponseTimer(callback);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Frames can be held forever if the Block Ack setup request is never sent on air

The deadline for giving up on a Block Ack setup is only armed when the request frame actually goes on air (agreement->setAddbaResponseDeadline(...) at src/inet/linklayer/ieee80211/mac/blockack/OriginatorBlockAckAgreementHandler.cc:222), so a setup whose request is discarded before transmission never expires.
Impact: All data to that receiver and priority stays stuck in the queue indefinitely, silently dropping the flow.

Path where the request is dropped before transmission

processTransmittedDataFrame creates the agreement and hands the ADDBA Request to processMgmtFrame, which enqueues it. From that moment the eligibility function in Hcf::initialize (src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc:63-66) holds all QoS data for that receiver/TID because isAddbaResponsePending() is true. The deadline, and hence computeEarliestAddbaResponseDeadline() (which requires getIsAddbaRequestSent()), only becomes active in processTransmittedAddbaReq. If the management frame is dropped before it is ever transmitted — e.g. retry-limit reached during internal collision handling in Hcf::handleInternalCollision (src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc:270-286) — the agreement stays pending with deadline -1 and is never cleaned up by addbaResponseTimeoutExpired. Arming the deadline at agreement creation (or removing the agreement when its ADDBA Request is dropped) would avoid the permanent stall.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked as Outdated by Devin

Comment on lines +62 to +71
Packet *packet = nullptr;
for (int i = 0; i < pendingQueue->getNumPackets(); i++) {
auto candidate = pendingQueue->getPacket(i);
if (isFrameEligible(candidate)) {
pendingQueue->removePacket(candidate);
packet = candidate;
break;
}
}
ASSERT(packet != nullptr);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Queue statistics for outgoing Wi-Fi frames are lost

Frames are now taken out of the transmit queue with a plain removal call (pendingQueue->removePacket(candidate) at src/inet/linklayer/ieee80211/mac/originator/OriginatorMacDataService.cc:66, and the same in the QoS variant) instead of the normal dequeue path, so the queue no longer reports the packet as served.
Impact: Recorded queueing-time and dequeue statistics for all Wi-Fi transmit queues disappear or change, even in configurations unrelated to Block Ack.

Difference between dequeuePacket() and removePacket()

PacketQueueBase::dequeuePacket() calls pullPacket() which stamps the queueing time tag, inserts the packet event and emits packetPulledSignal (src/inet/queueing/queue/PacketQueue.cc:117-133), which feeds @statistic[queueingTime]. removePacket() only emits packetRemovedSignal (src/inet/queueing/queue/PacketQueue.cc:135-143). The new selection loops in both OriginatorMacDataService::extractFramesToTransmit and OriginatorQosMacDataService::extractFramesToTransmit always use removePacket(), including the common case where the selected packet is the queue head and no eligibility filtering is in effect (the non-QoS DCF service never gets an eligibility function). Using dequeuePacket() when the chosen candidate is the head would preserve the previous statistics behaviour.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked as Outdated by Devin

Track each originator ADDBA setup with an exact local transaction ID, start the response timeout only after the complete request is transmitted, and apply a separate retry backoff after timeout, refusal, or discard. Cancel all queued and in-progress fragments belonging to failed or completed transactions so stale requests cannot block or later re-establish state.

Establish recipient Block Ack agreements when a request is accepted, remove response-packet staging, reset reorder buffers on renegotiation, and publish distinct agreement-change observations. Preserve internal-collision recovery and materialize frames only at DCF grants and HCF TXOP continuation boundaries so availability queries remain side-effect free.

Add typed queue drop callbacks and arbitrary-packet dequeue propagation through queues, flows, and schedulers. Restore QueueingTimeTag, PEK_QUEUED, and packetPulled accounting exactly once while keeping cancellation on removal/drop semantics.

Extend the focused regression with real fragmentation and transaction cancellation, reorder reset, queue accounting and overflow callbacks, HCF continuation, and legacy DCF channel-grant coverage.

Validation: release and debug builds; focused Ieee80211AddbaTransaction unit test; MacDcf and MacEdca smoke simulations; architecture and WLAN reviews; 22 related fingerprints with 15 unchanged and 7 explained Block Ack trajectory changes. Fingerprint baselines are intentionally unchanged.
Defer ADDBA initiation until the triggering QoS MPDU is acknowledged and its final fragment completes. This preserves the negotiated starting sequence number and prevents an outstanding retry or remaining fragment from falling below the recipient reorder window.

Resume eligible channel access when a dropped setup frame terminates a pending transaction, and cancel tagged ADDBA requests when DELBA tears down pending state. Restore DCF pending-frame materialization and defensively handle internal collisions with no selectable frame.

Use the configured EDCAF count for HCF queue traversal, track packet-drop callback registration, and unregister callbacks during teardown. Extend the focused unit test with ACK timing, fragmented MSDU, DELBA cancellation, queue wakeup, and DCF materialization coverage.

Validation: release build and focused Ieee80211AddbaTransaction_1 unit test pass; git diff --check passes. The full unit and fingerprint suites were not run.
Keep ADDBA setup requests and same-peer/TID data synchronized with the pending originator agreement. Harden cancellation, retry cleanup, DELBA teardown, reordering ownership, and in-progress frame disposal so stale setup traffic cannot be double-freed or leave protocol state behind.

Add an HCF-owned per-access-category eligibility index. Track exact queue departures through typed lifecycle callbacks and rebuild eligibility only when Block Ack state changes, making routine channel-access availability checks independent of pending queue depth.

Preserve configurable queue-provider policy during selective extraction and A-MSDU formation. Add predicate-aware Priority, WRR, Label, flow, leaf, and compound extraction; batch overflow notifications to avoid reentrant victim selection; and conservatively skip pre-extraction aggregation through transforming packet flows.

Document the incompatible queue contracts and DELBA standards trace. Expand focused coverage for ADDBA lifecycle transitions, direct and RTS failure cleanup, custom and compound queues, scheduler accounting, shared buffers, A-MSDU processing, and the no-scan eligibility invariant.

Validated with release/debug builds, focused unit tests in both modes, runtime-clean affected fingerprint simulations, and an independent INET/WLAN architecture review.
Prevent predicate-based extraction from bypassing closed gates or implicit guard bands by validating the exact upstream candidate before dequeueing it.

Drain PacketQueue instances completely during bulk removal and detach only the queue's own packets from shared buffers so removal callbacks remain complete and isolated.

Transfer buffered Block Ack frames to the recipient data service during reset, emit one packet-drop signal per discarded MPDU, and reclaim cancelled out-of-sequence ADDBA frames immediately.

Clear both short and long retry state on terminal frame discard, and make scheduler extractor requirements explicit while preserving PriorityScheduler's nullable collection accounting.

Add focused regression coverage for closed and guarded gates, multi-packet and shared-buffer removal, reorder-reset drop accounting, idle ADDBA cancellation, retry-state cleanup, and extractor-only priority inputs.

Validated with the focused Ieee80211AddbaTransaction_1 unit test in release and debug modes.
Reset recipient-side Block Ack reordering when a locally transmitted recipient DELBA removes an agreement. Keep the removed agreement alive through HCF notification so teardown observers receive valid state, and remove the unused ADDBA-response-sent flag.

Return removed recipient agreements from the transmitted-DELBA handler and cover teardown plus same-peer/TID reestablishment with a reorder-buffer regression. The test proves stale sequence state and buffered fragments do not leak into the replacement session.

Report shared PacketBuffer removals to PacketQueue observers exactly once. A dedicated pre-drop detach callback preserves batch removal semantics: ordinary removals report REMOVED, overload victims report DROPPED, and all selected victims are detached before drop callbacks can re-enter queue selection.

Add focused coverage for direct shared-buffer removal, overload callback reasons, recipient DELBA cleanup, and fresh reorder state after renegotiation.

Validation:

- Release and debug builds pass.

- Focused ADDBA unit tests pass in release and debug.

- The full release unit suite passes 78/90; the 12 failures are unrelated pre-existing clock and TCP cases.

- Focused Block Ack fingerprint mismatches are unchanged by an old-vs-new HCF channel-resumption A/B, so fingerprint baselines remain untouched.

- Architectural review reports no new violations.
@mgonzalezlopezudc
mgonzalezlopezudc force-pushed the fix-ieee80211-addba-transaction branch from 3a40816 to 2a389ec Compare August 18, 2026 12:03
Return removed originator agreements from transmitted DELBA processing so HCF can emit balanced agreement lifecycle signals only for agreements that were actually established.

Treat successful ADDBA responses as established before applying a local policy veto, then queue an initiator DELBA with END_BA. Handle transmitted and pre-transmission-aborted teardown frames idempotently, preserve retry suppression, and restore frame eligibility without leaving a blocked TID.

Make DROPPED and REMOVED unsent ADDBA requests terminal while keeping DEQUEUED as an ownership transfer. Count only actionable EDCA internal collisions and retain release/end-TXOP/all-AC resumption ordering without restarting active contention.

Rename addbaFailureTimeout to addbaResponseTimeout, document the API and configuration migration, and add focused debug/release coverage for lifecycle signals, queue removal, local veto teardown, retry backoff, collision filtering, and channel-access resumption.

Validation: debug and release builds pass; Ieee80211AddbaTransaction_1 passes in both modes; git diff --check passes. Fingerprint baselines are intentionally unchanged pending explicit acceptance of the attributed EDCAF timing shift.
Keep nullable packet collection and extractor capabilities in WrrScheduler, LabelScheduler, and PriorityScheduler. Validate aggregate and predicate operations lazily and consistently so ordinary passive sources remain valid scheduler inputs.

Make A-MSDU selection candidate-aware across flow modules and non-order-preserving schedulers. Remove selected members through exact predicate dequeues, preserve provider accounting, and revalidate aggregation-critical frame fields before building the aggregate.

Immediately roll back successful ADDBA agreements rejected by local policy while retaining Normal Ack data service. Track best-effort DELBAs by transaction, reject stale generations, handle fragmented transmit and abort lifecycles, and cancel obsolete queued or in-progress teardown packets before replacement setup.

Document the updated scheduler and aggregation contracts, update WHATSNEW, and add focused regression coverage for scheduler capabilities, flow and reverse-priority aggregation, agreement signal ordering, stale teardown disposal, fragmentation, and queue cleanup.
Keep transaction-tagged initiator DELBA frames eligible until a final-fragment acknowledgement or terminal abort, allowing failed management transmissions to follow the normal retry and retry-limit paths. Make teardown abort outcomes explicit in HCF and clean up sibling frames and acknowledgement state exactly once.

Preserve same-flow MSDU ordering during A-MSDU selection, make extraction contract validation exception-safe, propagate destructive drops through nested compound queues exactly once, reject unsupported PacketBuffer ownership before mutation, and make fragment tag propagation robust. Document the scheduler aggregate-query compatibility change.

Add focused coverage for DELBA retry, acknowledgement and retry-limit cleanup; malformed packet extractors; nested queue callbacks; PacketBuffer ownership; scheduler capabilities; and fragment tag propagation.

Validated with release and debug builds, focused Ieee80211AddbaTransaction_1 tests in both modes, git diff --check, and independent architecture and IEEE 802.11 semantic review. Fingerprint baselines are intentionally unchanged.
Propagate MAC duplicate detection from the recipient QoS data service into HCF so retransmitted management frames are not processed as new negotiations.

Cache the exact ADDBA response per originator and TID and replay it for a recognized duplicate while preserving the existing agreement, reorder window, buffered MPDUs, inactivity timer, and agreement signals. Fresh MAC identities still perform normal renegotiation, including when their ADDBA parameters are unchanged.

Clear replay state during agreement teardown and avoid retaining responses for initial rejected requests without an agreement. Add focused transaction coverage for accepted and rejected requests, duplicate response replay, genuine renegotiation, reorder-buffer preservation, DELBA handling, and cache lifecycle.
Forward REMOVED callbacks from descendant queues through CompoundPacketQueueBase so observers are notified whenever a packet leaves the enclosing logical queue, including leaf-initiated and shared-buffer removals.

Track the packet currently removed at the compound boundary with a scoped save-and-restore guard. Suppress only the matching descendant callback during boundary removal and overflow victim detachment, preserving exactly-once delivery and the intended DROPPED reason while allowing nested or reentrant removals of other packets to propagate.

Extend the ADDBA transaction unit coverage for direct and nested compounds, bulk and shared-buffer removal, boundary remove/dequeue/pull paths, reentrant removal, and compound capacity drops.

Validation: debug build; focused Ieee80211AddbaTransaction_1 unit test; focused PriorityQueue and EthernetQosQueue fingerprints; focused architecture check; independent semantic review.
Clarify agreement ownership, harden reentrant cleanup, restore queue extraction animation, and add focused regressions for the reviewed edge cases.
@mgonzalezlopezudc

mgonzalezlopezudc commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

@levy

The last commit contains the updated fingerprints mentioned in the following report.

Responses to the reviewer (Devin)

Devin agrees with this report

Date: 2026-08-22

Reviewed range: 7b0a6de5cef3e4515dedd542d742f469ab95daaa..e02c768a4bdd759687db68c3aef29fefb25bfce9

Latest fingerprint validation HEAD: e02c768a4bdd759687db68c3aef29fefb25bfce9

Scope: all twelve supplied final-review comments

Conclusion

The final review contains one useful defensive-hardening request, two intentional model/API-scope changes that should remain documented, one confirmed but non-normative reorder-buffer data-loss concern, one performance comment already addressed at the current HEAD, five correct informational observations, and two defect premises that do not hold for the current implementation.

No supplied comment demonstrates a current-path merge blocker. The two worthwhile follow-ups are:

  1. Make Hcf::cancelAddbaTransaction() structurally safe if a future caller violates the current invalidate-before-cancel ordering.
  2. Decide explicitly whether recipient teardown/renegotiation should deliver complete frames stranded behind a reorder-window gap before releasing the old buffer. The current drop is real, but IEEE Std 802.11-2024 does not explicitly require draining the buffer at teardown.

The eight directly related debug fingerprint rows whose expectations were updated in e02c768a4b now all pass at that exact HEAD. The earlier fingerprint blocker is therefore closed. The separate 5 Gbps half-duplex Ethernet initialization error from the previous broad run is unrelated to this branch and was not selected by the focused rerun.

# Reviewer comment Disposition Merge action
1 cancelAddbaTransaction() can retain a stale index under reentrancy Accept as low-severity hardening Prefer mutation-safe iteration plus a focused reentrancy test; not a demonstrated current-path blocker.
2 ADDBA starts only after an acknowledged QoS data frame Partly accept; intentional model scope Keep unless automatic setup for custom NO_ACK-only traffic is required; document and test the scope.
3 HCF leaves dangling callbacks during teardown Reject the defect premise Do not traverse EDCA from ~Hcf(); optional lifecycle symmetry belongs before child deletion.
4 PriorityScheduler aggregate queries now abort Partly accept; intentional documented break Keep fail-fast behavior and migration text; no affected shipped topology was found.
5 Recipient teardown drops buffered reorder frames Partly accept; confirmed loss, overstated standards premise Non-blocking design follow-up: choose drop-all or deliver-complete semantics and add a focused test for that choice.
6 Stale-ADDBA failure paths are narrowly reachable Confirm INFO Keep the paths and their active-frame-sequence invariant.
7 Local veto establishes and immediately tears down Confirm INFO Keep the standards-aligned ordering and balanced signals.
8 A-MSDU aggregation assumes stable enumeration/extraction Confirm contract observation No change; unstable providers fail explicitly instead of silently mis-aggregating.
9 Aggregation validation is expensive Resolved at current HEAD 254b5a01da removed whole-queue snapshot enumeration; retain the remaining release-mode guards and profile before further work.
10 PacketBuffer rejects unsupported cPacketQueue owners Accept as intentional compatibility break No change; the rejection prevents owner-queue corruption.
11 PacketQueue::removeAllPackets() isolates shared-buffer queues Confirm correctness fix Merge as-is.
12 Received recipient DELBA is keyed by transmitter Confirm latent bug fix Merge as-is.

Detailed responses

1. Reentrancy of cancelAddbaTransaction() through REMOVED

Response: technically valid low-severity hardening; not a currently reachable defect.

Hcf::cancelAddbaTransaction() initializes its reverse-loop index from the queue size once (Hcf.cc:309). pendingQueue->removePacket() (:312) synchronously invokes the registered queue callback through PacketQueue.cc:167-175 and PacketQueueBase.cc:73-76. If that callback removed further entries reentrantly, the outer getPacket(i) could use an index based on the former size.

Every current production caller prevents recursion by invalidating the transaction before cancellation: the agreement is erased or terminated, changed from pending to established, or the teardown mapping is erased (OriginatorBlockAckAgreementHandler.cc:77-96, :227-231, :253-284, and :328-409). The synchronous callback therefore finds neither a pending setup nor a pending teardown for the same transaction.

Recommended action: make the scan mutation-safe rather than relying only on caller ordering. A focused regression should deliberately cancel while state is still pending with at least three same-transaction packets and verify no out-of-range access, exactly-once removal/drop signals, and consistent eligibility accounting. This is advisable hardening for a new transaction API, but it is not a demonstrated current-path blocker.

2. ADDBA setup is triggered only after an acknowledged QoS data frame

Response: partly accepted; the behavior change is intentional and narrower than stated.

The current path starts setup only after a successful Normal ACK of the final fragment (Hcf.cc:840-878), and OriginatorBlockAckAgreementHandler::processAcknowledgedDataFrame() advertises the acknowledged sequence number plus one (OriginatorBlockAckAgreementHandler.cc:207-242). This supplies an authoritative SSN.

Two examples in the comment are not new regressions:

  • multicast already bypassed the old unicast setup hook (Hcf.cc:671-677);
  • the default OriginatorQosAckPolicy returns NORMAL_ACK before an agreement exists (OriginatorQosAckPolicy.cc:97-108), so BlockAck-acknowledged traffic cannot bootstrap the default agreement path.

The actual omitted case is a custom policy that marks otherwise eligible unicast QoS traffic exclusively NO_ACK before an agreement exists. IEEE Std 802.11-2024 clause 10.25.2 (80211ax-2024:chunk:05343), Table 9-466 (chunk:04239), and clause 11.5.2.2 (chunk:06199) define setup and SSN semantics, but do not require a preceding data ACK. This is an INET initiation-policy decision, not a standards violation.

Recommended action: retain the implementation unless custom NO_ACK automatic setup is a supported requirement. Document the trigger scope and add a focused custom-policy test. Supporting the omitted case should use authoritative sequence-allocation state, not simply restore the old transmitted-frame hook.

3. HCF remains registered on child pending queues at destruction

Response: reject the alleged dangling-pointer window.

The premise reverses OMNeT++ module deletion order. cModule::doDeleteModule() deletes submodules before executing delete this (omnetpp-6.4.0aipre2/src/sim/cmodule.cc:117-169). HCF is therefore alive while its EDCA and pending-queue descendants are destroyed. By the time Hcf::~Hcf() runs, those children are gone, which is why Hcf.cc:1066-1081 correctly avoids traversing EDCA.

Current queue destruction also does not fire INET removal callbacks: PacketQueue::~PacketQueue() only deletes its dropper, its cPacketQueue clears through the OMNeT++ queue implementation without notifyPacketRemoved(), and the callback vector is simply destroyed.

Required action: none. Do not unregister from ~Hcf(). Optional symmetric cleanup could be implemented in an idempotent finish()/preDelete() path while descendants still exist, with a dynamic-deletion regression, but it is not needed for current behavior.

4. PriorityScheduler aggregate queries now abort

Response: valid compatibility observation; no shipped-topology defect found.

IPacketCollection requires exact nonnegative aggregate values, so the former -1 sentinel violated the interface. PriorityScheduler.cc:28-49 now rejects aggregate access when any provider lacks IPacketCollection; ordinary priority scheduling still needs only passive providers (:139-146). The migration is documented in WHATSNEW:135-142 and doc/src/users-guide/ch-diffserv.rst:284-289.

The runtime consequence is real for an external finite-capacity compound queue: CompoundPacketQueueBase::isOverloaded() queries the enabled packet/data capacity, including the final debug assertion on a push. The old sentinel silently disabled capacity enforcement; the new behavior aborts instead of pretending an exact size was known.

Repository inspection found collection-capable provider chains in the shipped compound queues and their example, showcase, and tutorial uses. The directly instantiated tutorial schedulers use PacketQueue; Diffserv, TSN gating/shaping, PriorityQueue, CompoundPendingQueue, and EthernetQosQueue chains also expose IPacketCollection. EthernetPreemptingMacLayer uses ordinary scheduler pulling rather than compound aggregate access.

Required action: no source change. Keep the fail-fast behavior, migration guidance, and focused coverage (Ieee80211AddbaTransaction_1.test:2642-2671 and :3773-3799). External finite-capacity compound users must supply collection-capable providers.

5. Agreement teardown drops buffered reorder frames

Response: the loss is real, but the claimed normative requirement is not established.

BlockAckReordering::resetReceiveBuffer() flattens and removes the entire old receive buffer (BlockAckReordering.cc:188-198). RecipientQosMacDataService::resetBlockAckReordering() then emits OTHER_PACKET_DROP and deletes every extracted MPDU (RecipientQosMacDataService.cc:33-44) without checking whether an MSDU is complete or passing it through defragmentation/A-MSDU deaggregation.

A complete frame can remain buffered legitimately when an earlier sequence number is missing. For example, with WinStartB = 0, a complete unfragmented MPDU at SN 1 is held while SN 0 is absent. On DELBA or accepted renegotiation, the old hole can no longer be filled, but reset deletes SN 1 rather than considering it for delivery. The branch newly applies this reset to accepted renegotiation and locally transmitted recipient DELBA; received-DELBA reset behavior already existed before this review range.

IEEE Std 802.11-2024 does not explicitly require draining every complete buffered MSDU during teardown:

  • clause 10.25.4 (80211ax-2024:chunk:05345) requires release of Block Ack resources;
  • clause 10.25.2 (chunk:05343) models successful modification as DELBA immediately before the replacement ADDBA;
  • clause 11.5.3.5 (chunk:06213) requires updating recipient last-sequence state;
  • clauses 10.25.6.6.1, 10.25.6.6.2, and 10.25.6.6.3 (chunks:05358, :05360, and :05363) require ordered pass-up and explicitly pass complete frames when MPDU/BAR processing advances the window, but do not specify a teardown drain.

Accordingly, this is a credible avoidable-loss/model-quality concern, not a demonstrated IEEE violation. Existing tests at Ieee80211AddbaTransaction_1.test:1802-1860 and :1950-2004 intentionally assert the current drop semantics.

Recommended action: record an explicit design decision. If the project prefers loss-minimizing teardown, split complete from incomplete buffered MSDUs, pass complete ones upward in sequence order while allowing gaps, and drop only incomplete remnants. Otherwise document that releasing a reorder context discards everything stranded behind a gap. In either case, add a focused test for complete and fragmented buffered MSDUs. This is non-blocking absent a project requirement for lossless teardown.

6. Stale-ADDBA discard paths are reachable only for active-sequence frames

Response: confirmed INFO; the paths are necessary.

InProgressFrames::getFrameToTransmit() rejects ineligible frames (InProgressFrames.cc:81-88), while HCF eligibility rejects stale ADDBA requests (Hcf.cc:66-75). cancelAddbaTransaction() deliberately skips packets borrowed by the active frame sequence (Hcf.cc:256-270 and :300-304).

The stale guards in originatorProcessRtsProtectionFailed() and originatorProcessFailedFrame() therefore clean exactly the already-borrowed packets excluded from eager cancellation. Other stale siblings are removed eagerly. Coverage exists for active direct/RTS retention, unreferenced eager cleanup, and stale RTS terminal cleanup (Ieee80211AddbaTransaction_1.test:1279-1345 and :3222-3268).

Required action: keep both paths and the exclusion invariant. A direct unprotected stale-failure case would close the remaining narrow test gap.

7. Local-veto ADDBA establishes then immediately tears down

Response: confirmed INFO; current ordering is internally consistent and standards-aligned.

A matching status-zero response first establishes the agreement, then a custom local policy may reject its negotiated parameters, remove it, record retry backoff, and enqueue initiator DELBA with RC_END_BA (OriginatorBlockAckAgreementHandler.cc:245-277). The default policy accepts status zero (OriginatorBlockAckAgreementPolicy.cc:42-45), so this branch requires customization.

IEEE Std 802.11-2024 clause 10.25.2 Note 3 (80211ax-2024:chunk:05343) permits deleting a successfully negotiated agreement whose parameters are locally unacceptable and continuing with Normal Ack. The inactivity timer stores no agreement pointer; expiration scans the live agreement map (OriginatorBlockAckAgreementHandler.cc:99-117), so a leftover event can only no-op or reschedule from live state. HCF's Added-then-Deleted signals keep the active-agreement statistic neutral.

Required action: none. Focused tests cover veto/DELBA/backoff and signal order (Ieee80211AddbaTransaction_1.test:1154-1205 and :2192-2208).

8. A-MSDU aggregation assumes stable enumeration and exact extraction

Response: correct contract observation; not a defect in shipped providers.

BasicMsduAggregationPolicy::computeAggregateFrames() anchors on the provider-selected candidate, scans only the enumeration suffix, and deliberately does not wrap because collection order need not equal scheduling order (BasicMsduAggregationPolicy.cc:59-112). aMsduAggregateIfNeeded() then requires each selected pointer to remain present, unique, eligible, and unchanged while it is extracted by exact-pointer predicate (OriginatorQosMacDataService.cc:38-85).

This assumes stable selected identities and critical fields during the synchronous operation, but not perfectly stable enumeration. Unrelated ordering and noncritical content may change. No simulation event can interleave these direct calls. A provider that makes a selected identity disappear, returns a different packet for an exact predicate, or mutates an aggregation-critical field during extraction violates the selection/extraction contract. Throwing is preferable to silently aggregating the wrong MSDUs or corrupting ownership.

Required action: none. Keep the release-mode checks. The built-in policy and shipped queue/provider implementations satisfy the assumption, and focused malformed/repeated/compound-provider tests exercise the failure contract. If third-party providers need looser semantics, the interface contract should be revised explicitly rather than weakening validation locally.

9. Aggregation validation cost

Response: the specific whole-queue-enumeration concern is obsolete at current HEAD.

The reviewer accurately describes the earlier implementation, but commit 254b5a01da removed the whole pending-queue unordered_set snapshot. Current validation performs an identity findPacket() for each selected subframe, snapshots aggregation-critical fields, extracts each by exact predicate, and revalidates afterward (OriginatorQosMacDataService.cc:38-85). The new regression uses two selected packets with a 128-packet trailing backlog and verifies zero packet-collection enumeration calls (Ieee80211AddbaTransaction_1.test:2850-2883).

The remaining generic worst case is provider-dependent: k identity lookups and k exact dequeues can cost O(k*n) for linear providers. The built-in policy's indexed getPacket(i) enumeration over a linked cPacketQueue can itself be Theta(n^2); that behavior predates these guards, and the current validation does not worsen the asymptotic bound. Custom policies can validate and extract through IPacketExtractor without collection enumeration. In practice, k is bounded by A-MSDU size and the default pending queue is bounded. The optimization commit records an approximately fivefold improvement for the affected MacQos runs.

Required action: retain the checks. Do not gate extension-boundary correctness behind debug mode. Profile fixed-seed aggregation-heavy simple and compound queues before considering a shared snapshot/index contract or another optimization.

10. PacketBuffer rejects unsupported cPacketQueue owners

Response: valid backward-compatibility note; the fail-fast behavior is a correctness improvement.

PacketBuffer::addPacket() now rejects, before mutating the buffer, a packet owned by a cPacketQueue whose owner does not implement IPacketBuffer::ICallback (PacketBuffer.cc:53-61). Standard PacketQueue implements that callback. The overflow path's check_and_cast is therefore protected by the upfront invariant.

The old null-tolerant path was unsafe: a selected victim could be taken and deleted while an unsupported owner queue retained its pointer. Silently skipping the callback could leave owner-queue corruption and a later use-after-free. WHATSNEW:149-154 documents the break, and the focused regression verifies rejection before buffer mutation (Ieee80211AddbaTransaction_1.test:3588-3611).

Required action: none. Third-party/manual cPacketQueue owners must implement IPacketBuffer::ICallback or avoid using the shared PacketBuffer. Packets not owned by a cPacketQueue remain accepted.

11. PacketQueue::removeAllPackets() no longer flushes a shared buffer

Response: confirmed correctness improvement; no further action.

PacketQueue.cc:178-192 drains the local cPacketQueue, removes exactly those pointers from the optional buffer, then emits this queue's removal callback/signal and deletes the packets. After queue.pop(), the packet is no longer owned by a cPacketQueue, so PacketBuffer::removePacket() only erases the registry entry and cannot reenter the former owner. The buffer does not delete ordinary removals, so there is no double-free.

The change also fixes the old shrinking-loop bug, where reevaluating getNumPackets() while popping could leave packets behind. The shared-buffer regression at Ieee80211AddbaTransaction_1.test:3536-3585 verifies that clearing one queue retains another queue's packet and that later direct buffer removal notifies the remaining owner once.

Required action: merge as-is.

12. Recipient DELBA keying changed from receiver to transmitter

Response: confirmed latent bug fix; no action.

Recipient agreements are created under (ADDBA Request transmitter, TID) (RecipientBlockAckAgreementHandler.cc:120-151). For a received originator-initiated DELBA, the remote originator is the DELBA transmitter, so removal at RecipientBlockAckAgreementHandler.cc:178-182 must use getTransmitterAddress(). HCF resets reorder state with the same (TID, transmitter) pair (Hcf.cc:574-582).

The asymmetry is correct: the peer of a locally transmitted recipient DELBA is its receiver, so processTransmittedDelba() uses getReceiverAddress() (RecipientBlockAckAgreementHandler.cc:173-175). IEEE Std 802.11-2024 Figure 9-154 and clauses 9.4.1.16 and 10.25.4 support these direction semantics. Exact coverage is at Ieee80211AddbaTransaction_1.test:1872-1889.

Required action: merge as-is.

Validation evidence

Working directory: /home/user/omnetpp_ws/inet-addba-transaction/tests/fingerprint

Full-suite run and fingerprint update history

Before updating the expected fingerprints, the full debug fingerprint suite was run at 254b5a01da6cf3872a917407da9de157cda0f7a1, with all CSV rows selected and only the three maintained ingredients used for comparison:

./fingerprinttest -d -m '.*' \
  -f 'tplx' -f '~tNl' -f '~tND' \
  -l fingerprinttest-20260821-1100.out

The run selected 1,753 rows: 1,744 verified successfully, eight produced fingerprint mismatches, and one stopped during initialization. The eight mismatches were:

Test row Previously recorded Calculated by the full-suite run
tests/fingerprint/examples.csv:661MacQosWithBlockAck 8306-3cd3/tplx;26d1-9165/~tNl;e0fc-7553/~tND 040e-6428/tplx;557f-bcad/~tNl;68e6-5a6d/~tND
tests/fingerprint/examples.csv:662MacQosWithTransactionalBlockAck c4ff-d71f/tplx;68ba-2827/~tNl;9a98-cc02/~tND b608-4cad/tplx;6f80-132d/~tNl;9b78-3d6c/~tND
tests/fingerprint/showcases.csv:202NoFragmentation aa2d-5d35/tplx;2094-1f2a/~tNl;1470-1e1b/~tND 7ecf-66f5/tplx;193b-2155/~tNl;fa4f-92fc/~tND
tests/fingerprint/showcases.csv:203Fragmentation 7ae9-e07d/tplx;db8b-3b81/~tNl;9c41-dc97/~tND fda2-be01/tplx;be55-bf3e/~tNl;0edc-72bf/~tND
tests/fingerprint/showcases.csv:204MixedTraffic 462d-10c7/tplx;727b-d26a/~tNl;62c4-cbc2/~tND 3530-5a7d/tplx;4bee-ff14/~tNl;7588-8d92/~tND
tests/fingerprint/showcases.csv:270HCFfragblockack 41c4-d741/tplx;db22-9b02/~tNl;523f-f640/~tND 8557-5e23/tplx;fabc-bff1/~tNl;9ab7-ce7d/~tND
tests/fingerprint/showcases.csv:344txop/General 9fdc-f33e/tplx;5ee8-bcc1/~tNl 0a69-2e4d/tplx;fe18-b226/~tNl
tests/fingerprint/examples.csv:9adhoc/qos MacQos -r 1 ed2f-2d62/tplx;8a06-c4b3/~tNl;cddb-a568/~tND 3d9e-813a/tplx;e30d-288b/~tNl;b8e0-ee55/~tND

The failures were consistent with the intentional trajectory-changing parts of the branch:

  • Block Ack transaction state, response handling, and final-fragment processing changed in OriginatorBlockAckAgreementHandler and Hcf, directly affecting the Block Ack examples and showcases.
  • QoS pending-frame eligibility and A-MSDU selection/extraction changed in OriginatorQosMacDataService and BasicMsduAggregationPolicy, affecting QoS queue selection and event/packet trajectories.
  • TXOP continuation changed in HcfFs, accounting for the showcases/wireless/txop trajectory change.
  • MacQosWithBlockAck now receives sameTransmissionStartTimeCheck = "ignore" directly in its configuration, changing that configuration's recorded trajectory.
  • Both adhoc/qos repetitions exercise the revised QoS/HCF path through the configuration's ${false, true} Block Ack variation; only repetition 1 enables that variation and mismatched, while repetition 0 retained its prior expectation.

These mechanisms explain why the affected fingerprints changed and why the mismatches were confined to the related QoS, Block Ack, fragmentation, and TXOP rows. The earlier assessment did not claim that the first event-level divergence had been independently localized for every one of the eight rows.

The remaining full-suite failure was unrelated to the branch: the existing tests/fingerprint/ethernet.csv row with $datarate == 5Gbps && $duplex == false failed during initialization because EthernetCsmaMacPhy rejects 5 Gbps half-duplex Ethernet. It produced no replacement fingerprint.

After assessing the eight trajectory changes, their expected values were updated in tests/fingerprint/examples.csv and tests/fingerprint/showcases.csv by commit e02c768a4b. No expectation was added for the unrelated Ethernet initialization failure.

Post-update focused confirmation

The same eight changed rows were then rerun at e02c768a4b:

./fingerprinttest -d \
  -m '/examples/adhoc/qos/ .*MacQos -r 1' \
  -m '/examples/wireless/qos/ .*MacQosWith(BlockAck|TransactionalBlockAck)' \
  -m '/showcases/wireless/blockack/' \
  -m '/showcases/wireless/fragmentation/ .*HCFfragblockack' \
  -m '/showcases/wireless/txop/' \
  -f 'tplx' -f '~tNl' -f '~tND' \
  -l fingerprinttest-final-comments-20260822.out

Result at e02c768a4b: exit status 0; eight tests selected; eight passed; no mismatches. The saved artifact is tests/fingerprint/fingerprinttest-final-comments-20260822.out. The wrapper used debug runners via -d; no release artifact was executed.

Together, the two runs establish the sequence explicitly: the full suite exposed eight related stale expectations; those eight expectations were updated; and the exact affected rows subsequently passed against the updated baselines.

No new unit test was launched for this report. Existing unit_tests.log records the explicitly filtered debug Ieee80211AddbaTransaction_1.test passing on 2026-08-22. It covers the existing transaction ordering, ACK/final-fragment, stale active-sequence, local-veto, queue compatibility, shared-buffer, and reorder-reset behavior, but not deliberate pending-state reentrant cancellation or a deliver-complete teardown alternative.

The IEEE corpus status was fresh for IEEE Std 802.11-2024 (80211ax-2024); PDF inspection was not needed.

Architectural review

Applicable requirements include R-SCOPE-WIRELESS, R-RUN-REPRO, R-DIST-COMPAT, AR-ORG-CONTRACTS, AR-QUEUE-ROLES, AR-QUAL-LOGGING, AR-QUAL-TESTS, AR-QUAL-DETERMINISM, AR-WLAN-STD-TRACE, AR-WLAN-ARCH-OWNERSHIP, AR-WLAN-MAC-EXCHANGE, AR-WLAN-MAC-SEQUENCE, AR-WLAN-MAC-QOS, AR-WLAN-OBS-EVENTS, and AR-WLAN-QUAL-TESTS.

No new AV-*, AS-*, NV-*, or NS-* disposition is needed. The comments do not require a source or sealing change.

General semantic checklist

  • PASS — AR-ORG-VIS-SPLIT — No protocol visualization logic was introduced.
  • PASS — AR-ORG-KERNEL — The changes consume OMNeT++ lifecycle and queue facilities without reimplementing them.
  • PASS — AR-MOD-COMPOSITION — Transaction, agreement, queue, aggregation, and reorder responsibilities remain separated.
  • PASS — AR-COM-SOCKETS — No application/transport socket interaction is involved.
  • PASS — AR-COM-DIRECT — No zero-time message substitutes for direct intra-node coordination.
  • PASS — AR-OBS-NED-TRUTH — NED remains authoritative for parameters, signals, and statistics.
  • PASS — AR-OBS-INTROSPECTION — No on-air protocol lacks introspection support.
  • PASS — AR-CFG-INFER / DRY — Transaction identity and agreement state remain centralized.
  • PASS — AR-CFG-PARAMS — No malformed parameter contract is introduced.
  • PASS — AR-EXT-NOCORE — No protocol addition modifies core registration machinery.
  • PASS — AR-BUILD-DECLARATIVE — No machine-specific build value was added.
  • PASS — AR-QUAL-NAMING — New semantic names follow the conventions.
  • PASS — AR-QUAL-LOGGING — Provider and invariant violations throw instead of merely logging.
  • PASS — AR-QUAL-TESTS — Focused correctness tests accompany the changed contracts; the two proposed hardening/design alternatives need tests only if implemented.
  • PASS — AR-QUAL-DISPLAY — No new module type lacks a distinguishing display declaration.

REVIEW: 15 PASS, 0 FLAG, 0 QUESTION

IEEE 802.11 semantic checklist

  • PASS — AR-WLAN-STD-TRACE — Setup, SSN, veto, retry, reorder, and teardown decisions are traceable to IEEE Std 802.11-2024; teardown draining is correctly identified as unspecified.
  • PASS — AR-WLAN-STD-GATING — Block Ack behavior remains gated by configured support, policy, and agreement state.
  • PASS — AR-WLAN-ARCH-BOUNDARIES — MAC, agreement policy, handler, queue, aggregation, and data-service responsibilities remain separated.
  • PASS — AR-WLAN-ARCH-OWNERSHIP — Agreement, transaction, sequence, retry, queue, and reorder state each retain one owner.
  • PASS — AR-WLAN-ARCH-VARIANTS — Negotiation decisions remain replaceable policies.
  • PASS — AR-WLAN-FRAME-REPRESENTATION — ADDBA/DELBA remain typed on-air chunks; transaction identity remains local metadata.
  • PASS — AR-WLAN-PHY-AUTHORITY — No PHY mode calculation is duplicated in MAC code.
  • PASS — AR-WLAN-PHY-TIMING — No PHY or interframe timing constant is duplicated.
  • PASS — AR-WLAN-MAC-EXCHANGE — Setup, response, timeout, retry, and teardown retain explicit owners.
  • PASS — AR-WLAN-MAC-SEQUENCE — SSN and reorder-window paths use centralized cyclic sequence handling.
  • PASS — AR-WLAN-MAC-QOS — TID/agreement lookup and per-AC queue state remain centralized.
  • PASS — AR-WLAN-MAC-MULTIUSER — No MU scheduling or PPDU construction is changed.
  • PASS — AR-WLAN-OBS-EVENTS — Agreement lifecycle and packet-drop events are emitted by their owners.
  • QUESTION — AR-WLAN-QUAL-TESTS — Confirm the intended model scope for custom pre-agreement NO_ACK traffic and the desired handling of complete frames stranded at recipient reorder teardown; add focused tests if either policy changes.

WLAN REVIEW: 13 PASS, 0 FLAG, 1 QUESTION

Final merge recommendation

The twelve final-review comments do not establish a current merge blocker. The directly related updated fingerprints pass in debug mode at e02c768a4b. Prefer hardening reentrant cancellation in this PR if the callback API is expected to tolerate arbitrary caller ordering; otherwise document the invalidate-before-cancel invariant. Treat recipient reorder draining as an explicit model-policy decision, because the loss is real but the claimed IEEE teardown mandate is not.

Avoid building a complete snapshot of the pending queue for every successful A-MSDU aggregation. Validate each policy-selected subframe through the packet extractor's identity lookup instead, while retaining duplicate detection, eligibility checks, pre-mutation validation, and existing RAII ownership handling.

Add a focused regression with two selected frames and a 128-packet trailing backlog. Verify that aggregation removes only the selected frames and performs no packet-collection enumeration.

Debug validation:

- make MODE=debug -j$(nproc)

- inet_run_unit_tests -m debug -f 'Ieee80211AddbaTransaction_1\.test'

- MacQos runs 0 and 1 to 1.1 s: approximately 5x faster

- focused fingerprints: run 0 unchanged; run 1 completes without the former CPU timeout and has a reproducible pre-existing ADDBA trajectory mismatch unrelated to this optimization
The latest full debug run used only `tplx`, `~tNl`, and `~tND` and selected 1,753 rows: 1,744 verified, eight mismatched, and one with an initialization error. The eight mismatches are:

| Test row | Recorded | Current calculated |
|---|---|---|
| `tests/fingerprint/examples.csv:661` — `MacQosWithBlockAck` | `8306-3cd3/tplx;26d1-9165/~tNl;e0fc-7553/~tND` | `040e-6428/tplx;557f-bcad/~tNl;68e6-5a6d/~tND` |
| `tests/fingerprint/examples.csv:662` — `MacQosWithTransactionalBlockAck` | `c4ff-d71f/tplx;68ba-2827/~tNl;9a98-cc02/~tND` | `b608-4cad/tplx;6f80-132d/~tNl;9b78-3d6c/~tND` |
| `tests/fingerprint/showcases.csv:202` — `NoFragmentation` | `aa2d-5d35/tplx;2094-1f2a/~tNl;1470-1e1b/~tND` | `7ecf-66f5/tplx;193b-2155/~tNl;fa4f-92fc/~tND` |
| `tests/fingerprint/showcases.csv:203` — `Fragmentation` | `7ae9-e07d/tplx;db8b-3b81/~tNl;9c41-dc97/~tND` | `fda2-be01/tplx;be55-bf3e/~tNl;0edc-72bf/~tND` |
| `tests/fingerprint/showcases.csv:204` — `MixedTraffic` | `462d-10c7/tplx;727b-d26a/~tNl;62c4-cbc2/~tND` | `3530-5a7d/tplx;4bee-ff14/~tNl;7588-8d92/~tND` |
| `tests/fingerprint/showcases.csv:270` — `HCFfragblockack` | `41c4-d741/tplx;db22-9b02/~tNl;523f-f640/~tND` | `8557-5e23/tplx;fabc-bff1/~tNl;9ab7-ce7d/~tND` |
| `tests/fingerprint/showcases.csv:344` — `txop/General` | `9fdc-f33e/tplx;5ee8-bcc1/~tNl` | `0a69-2e4d/tplx;fe18-b226/~tNl` |
| `tests/fingerprint/examples.csv:9` — `adhoc/qos MacQos -r 1` | `ed2f-2d62/tplx;8a06-c4b3/~tNl;cddb-a568/~tND` | `3d9e-813a/tplx;e30d-288b/~tNl;b8e0-ee55/~tND` |

The direct branch mechanisms justify investigating these changes: Block Ack transaction state and final-fragment handling changed in `OriginatorBlockAckAgreementHandler` and `Hcf`; QoS queue eligibility and A-MSDU extraction changed in `OriginatorQosMacDataService` and `BasicMsduAggregationPolicy`; TXOP continuation changed in `HcfFs`; and `MacQosWithBlockAck` now receives `sameTransmissionStartTimeCheck = "ignore"` directly in its configuration. Both adhoc repetitions exercise the revised QoS/HCF path with the configuration's `${false, true}` Block Ack variation, but only repetition 1 mismatches in the latest run. These mechanisms explain why trajectories can change, but the first event-level divergence has not yet been localized for every failing row.
@mgonzalezlopezudc

mgonzalezlopezudc commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Devin's response to that report:

The report's key claim verifies. All eight fingerprint rows the report says were updated in e02c768a4b show matching before→after edits in the PR diff, and the "after" values exactly match the report's "Calculated by the full-suite run" column:

  • tests/fingerprint/examples.csv:661MacQosWithBlockAck040e-6428/tplx;557f-bcad/~tNl;68e6-5a6d/~tND
  • tests/fingerprint/examples.csv:662MacQosWithTransactionalBlockAckb608-4cad/tplx;6f80-132d/~tNl;9b78-3d6c/~tND
  • tests/fingerprint/examples.csv:10adhoc/qos MacQos -r 13d9e-813a/tplx;e30d-288b/~tNl;b8e0-ee55/~tND
  • tests/fingerprint/showcases.csv:202-204 — NoFragmentation/Fragmentation/MixedTraffic ✓
  • tests/fingerprint/showcases.csv — HCFfragblockack → 8557-5e23/... and txop/General → 0a69-2e4d/...

Two details worth noting, both consistent with the report:

  • The -r 0 adhoc/qos row (tests/fingerprint/examples.csv:9) was not changed — matching the report's claim that only repetition 1 exercises the ${false, true} Block Ack variation and mismatched.
  • Rows with a tyf component (MacQosWithBlockAck, HCFfragblockack, txop/General, adhoc/qos) preserved that unfiltered component unchanged while only the three maintained ingredients (tplx, ~tNl, ~tND) were rebaselined — correct discipline for a -f 'tplx' -f '~tNl' -f '~tND' run.

Overall assessment of the final report

The prior merge blocker is genuinely closed. The stale expectations the previous report flagged are now updated in the diff, and the "after" values match the recomputed fingerprints. What I can independently confirm is that the CSVs were edited to the claimed values; what I cannot verify from the diff is the report's assertion that the focused rerun at e02c768a4b returned exit 0 with 8/8 passing — that's an environment-run claim resting on the same debug build. But it's self-consistent: if the recomputed hashes were written back as expectations, a rerun on the same build passing is the expected outcome.

The dispositions are well-calibrated. The two open items are correctly classified as non-blocking:

  1. Reentrant cancelAddbaTransaction() (Comment 1) — matches my own read: every current caller invalidates the transaction (erases/terminates the agreement or teardown mapping) before cancellation, so the synchronous handlePacketRemoved callback finds nothing pending and doesn't recurse. Low-severity hardening for a new API, not a live defect.

  2. Reorder-buffer drop at teardown (Comment 5) — the report's most honest revision. It concedes the loss is real (complete MPDUs stranded behind a gap are dropped at src/inet/linklayer/ieee80211/mac/recipient/RecipientQosMacDataService.cc:33-44 via src/inet/linklayer/ieee80211/mac/blockackreordering/BlockAckReordering.cc) but correctly downgrades the "IEEE requires draining" premise to unproven. That's the right call — 802.11 clause 10.25.4 requires resource release, not a teardown drain. Framing it as an explicit model-policy decision rather than a bug is defensible.

Points I'm taking on trust (not verifiable from the diff):

  • The OMNeT++ deletion-order citation (cmodule.cc:117-169) underpinning the Comment 3 rejection. It's the correct thing to hinge on and consistent with the in-tree comment at src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc, but I have not read the kernel source.
  • The 254b5a01da optimization claim (Comment 9) removing the whole-queue snapshot — plausible and matches the current src/inet/linklayer/ieee80211/mac/originator/OriginatorQosMacDataService.cc shape, but I haven't diffed that specific commit.
  • The run counts (1,744/8/1, then 8/8) are environment-dependent.

Bottom line: The report's conclusion — no current merge blocker, two non-blocking follow-ups (harden reentrant cancellation; decide teardown drain policy explicitly) — is well-supported. The fingerprint evidence, which was the only hard gate, checks out against the actual diff. The remaining AR-WLAN-QUAL-TESTS QUESTION (custom pre-agreement NO_ACK scope + teardown-drain policy) is correctly kept as a documentation/test decision rather than a code defect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant