Skip to content

Runtime error due to false Frame Sequence Handling in 802.11 HCF With QoS multiple Access Category #1026

Open
@albert-simohartono

Description

@albert-simohartono

Wi-Fi MAC uses FrameSequenceHandler with Transmit and Receive step. Transmit step used to start transmission and then changed to Receive step to wait for WlanAck.
When Receive step started, a timer startRxTimer is started. If the expected WlanAck is received on time, the timer will be cancelled and the next sequence step will start. Otherwise, handleStartRxTimeout() will be called and the current sequence will be aborted. Short time after the timeout, the NAV will sense that the medium is now free. The NAV timer endNavTimer was started at the same time as the startRxTimer.

In network with multiple wireless nodes, a node waiting for its WlanAck might receive another Ack intended for another node. When this happen, the running timer startRxTimer should still run, because we still waits for the correct WlanAck.

However, in Hcf::processLowerFrame(), the startRxTimer is always cancelled cancelEvent(startRxTimer) , even when receiving frame not for us. The current sequence will stuck in RX step for longer than expected. Only after the correct WlanAck received, the sequence will move to next step by calling frameSequenceHandler->processResponse(packet). The problem is that the correct WlanAck might takes longer time than the startRxTimer.

void Hcf::processLowerFrame(Packet *packet, const Ptr<const Ieee80211MacHeader>& header)
{
    Enter_Method("processLowerFrame(%s)", packet->getName());
    take(packet);
    EV_INFO << "Processing lower frame: " << packet->getName() << endl;
    auto edcaf = edca->getChannelOwner();
    if (edcaf && frameSequenceHandler->isSequenceRunning()) {
        // TODO always call processResponse?
        if ((!isForUs(header) && !startRxTimer->isScheduled()) || isForUs(header)) {
            frameSequenceHandler->processResponse(packet);
            updateDisplayString();
        }
        else {
            EV_INFO << "This frame is not for us" << std::endl;
            PacketDropDetails details;
            details.setReason(NOT_ADDRESSED_TO_US);
            emit(packetDroppedSignal, packet, &details);
            delete packet;
        }
        cancelEvent(startRxTimer);
    }
...
}

This became a problem when using EDCA with multiple AccessCategory AC. Each AC requests channel access for their own frame sequence.
When channel access for an AC is granted (after NAV timeout and decided that the medium is free), the frame sequence for that AC will start. At this point, none other frame sequence should still run. Otherwise the FrameSequenceHandler::startFrameSequence() will throw cRuntimeError("Channel access granted while a frame sequence is running").

This happens when the NAV timer endNavTimer expired and the RX timer startRxTimer cancelled, while the correct WlanAck not received yet. To avoid this, the cancelEvent(startRxTimer) should be placed inside the if-case. This way, if the correct WlanAck is not received on time, the startRxTimer will timeout and the current sequence will be aborted, before the NAV senses that the radio channel has become free.

    if (edcaf && frameSequenceHandler->isSequenceRunning()) {
        // TODO always call processResponse?
        if ((!isForUs(header) && !startRxTimer->isScheduled()) || isForUs(header)) {
            frameSequenceHandler->processResponse(packet);
            updateDisplayString();
            // Only cancel RxTimer when the current running sequence has been handled by frameSequenceHandler->processResponse().
            // If the received frame is not for us, we are still waiting to receive our ACK. In that case, don't cancel the timer.
            // Otherwise, current frame sequence stucks in RX step and runs longer than intendeed, preventing sequence from 
            // another access category (AC) to start running (RuntimeError("Channel access granted while a frame sequence is running")).
            cancelEvent(startRxTimer);
        }
        else {
            EV_INFO << "This frame is not for us" << std::endl;
            PacketDropDetails details;
            details.setReason(NOT_ADDRESSED_TO_US);
            emit(packetDroppedSignal, packet, &details);
            delete packet;
        }
    }

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions