Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
44 changes: 44 additions & 0 deletions src/inet/linklayer/ieee80211/mac/Ieee80211Duration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

#ifndef __INET_IEEE80211DURATION_H
#define __INET_IEEE80211DURATION_H

#include "inet/common/INETDefs.h"

namespace inet {
namespace ieee80211 {

// Only the ordinary 15-bit encoding denotes a reservation. Reserved and
// special Duration/ID values must not enter response-duration arithmetic.
inline simtime_t getIeee80211OrdinaryDuration(simtime_t duration)
{
return duration >= SIMTIME_ZERO && duration <= SimTime(32767, SIMTIME_US) ? duration : SIMTIME_ZERO;
}

inline simtime_t normalizeIeee80211Duration(simtime_t duration)
{
// IEEE Std 802.11-2024, 9.2.5.1: negative calculations become zero; fractions round upward.
if (duration <= SIMTIME_ZERO)
return SIMTIME_ZERO;
// 9.2.4.2, Table 9-9: ordinary Duration is 15 bits. AID is a separate representation.
if (duration > SimTime(32767, SIMTIME_US))
throw cRuntimeError("Calculated IEEE 802.11 Duration exceeds 32767 microseconds");
auto microseconds = duration.inUnit(SIMTIME_US);
if (SimTime(microseconds, SIMTIME_US) < duration)
microseconds++;
return SimTime(microseconds, SIMTIME_US);
}

inline uint16_t encodeIeee80211Duration(simtime_t duration)
{
if (duration < SIMTIME_ZERO)
throw cRuntimeError("Cannot serialize an unset or negative IEEE 802.11 Duration");
return static_cast<uint16_t>(normalizeIeee80211Duration(duration).inUnit(SIMTIME_US));
}

} // namespace ieee80211
} // namespace inet

#endif
15 changes: 8 additions & 7 deletions src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


#include "inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"

#include "inet/common/checksum/Checksum.h"
#include "inet/common/packet/serializer/ChunkSerializerRegistry.h"
Expand Down Expand Up @@ -185,7 +186,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
// variants -- not just plain Data (ST_DATA) and QoS-Data (ST_DATA_WITH_QOS).
if (macHeader->getFrameType() == 2) {
auto dataHeader = dynamicPtrCast<const Ieee80211DataHeader>(chunk);
stream.writeUint16Le(dataHeader->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(dataHeader->getDurationField()));
stream.writeMacAddress(dataHeader->getReceiverAddress());
stream.writeMacAddress(dataHeader->getTransmitterAddress());
stream.writeMacAddress(dataHeader->getAddress3());
Expand Down Expand Up @@ -222,7 +223,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
case ST_ACTION:
case ST_NOACKACTION: {
auto mgmtHeader = dynamicPtrCast<const Ieee80211MgmtHeader>(chunk);
stream.writeUint16Le(mgmtHeader->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(mgmtHeader->getDurationField()));
stream.writeMacAddress(mgmtHeader->getReceiverAddress());
stream.writeMacAddress(mgmtHeader->getTransmitterAddress());
stream.writeMacAddress(mgmtHeader->getAddress3());
Expand Down Expand Up @@ -298,7 +299,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
case ST_RTS: {
auto rtsFrame = dynamicPtrCast<const Ieee80211RtsFrame>(chunk);
stream.writeUint16Le(rtsFrame->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(rtsFrame->getDurationField()));
stream.writeMacAddress(rtsFrame->getReceiverAddress());
stream.writeMacAddress(rtsFrame->getTransmitterAddress());
if (stream.getLength() - startPos != rtsFrame->getChunkLength())
Expand All @@ -308,7 +309,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
case ST_CTS: {
auto ctsFrame = dynamicPtrCast<const Ieee80211CtsFrame>(chunk);
stream.writeUint16Le(ctsFrame->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(ctsFrame->getDurationField()));
stream.writeMacAddress(ctsFrame->getReceiverAddress());
if (stream.getLength() - startPos != ctsFrame->getChunkLength())
throw cRuntimeError("Cannot serialize the Ieee80211CtsFrame: chunkLength is %d B, does not match the %d B of serialized fields",
Expand All @@ -317,7 +318,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
case ST_ACK: {
auto ackFrame = dynamicPtrCast<const Ieee80211AckFrame>(chunk);
stream.writeUint16Le(ackFrame->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(ackFrame->getDurationField()));
stream.writeMacAddress(ackFrame->getReceiverAddress());
if (stream.getLength() - startPos != ackFrame->getChunkLength())
throw cRuntimeError("Cannot serialize the Ieee80211AckFrame: chunkLength is %d B, does not match the %d B of serialized fields",
Expand All @@ -326,7 +327,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
case ST_BLOCKACK_REQ: {
auto blockAckReq = dynamicPtrCast<const Ieee80211BlockAckReq>(chunk);
stream.writeUint16Le(blockAckReq->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(blockAckReq->getDurationField()));
stream.writeMacAddress(blockAckReq->getReceiverAddress());
stream.writeMacAddress(blockAckReq->getTransmitterAddress());
bool multiTid = blockAckReq->getMultiTid();
Expand Down Expand Up @@ -362,7 +363,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c
}
case ST_BLOCKACK: {
auto blockAck = dynamicPtrCast<const Ieee80211BlockAck>(chunk);
stream.writeUint16Le(blockAck->getDurationField().inUnit(SIMTIME_US));
stream.writeUint16Le(ieee80211::encodeIeee80211Duration(blockAck->getDurationField()));
stream.writeMacAddress(blockAck->getReceiverAddress());
stream.writeMacAddress(blockAck->getTransmitterAddress());
bool multiTid = blockAck->getMultiTid();
Expand Down
7 changes: 5 additions & 2 deletions src/inet/linklayer/ieee80211/mac/Rx.cc
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,11 @@ bool Rx::lowerFrameReceived(Packet *packet)
if (isFrameOk) {
EV_INFO << "Received frame from PHY: " << packet << endl;
const auto& header = packet->peekAtFront<Ieee80211MacHeader>();
if (header->getReceiverAddress() != address)
setOrExtendNav(header->getDurationField());
// IEEE Std 802.11-2024, 9.2.4.2: bit 15 distinguishes special
// Duration/ID encodings; they are not ordinary NAV intervals.
auto duration = header->getDurationField();
if (header->getReceiverAddress() != address && duration >= SIMTIME_ZERO && duration <= SimTime(32767, SIMTIME_US))
setOrExtendNav(duration);
return true;
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ std::vector<Packet *> *MsduDeaggregation::deaggregateFrame(Packet *aggregatedFra
if (header->getToDS() && header->getFromDS())
header->addChunkLength(B(6));
header->setTid(tid);
header->setDurationField(amsduHeader->getDurationField());
header->setSequenceNumber(SequenceNumberCyclic(0));
setExplodedFrameAddress(header, msduSubframeHeader, amsduHeader);
frame->insertAtFront(header);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//


#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"
#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.h"

#include "inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckAgreement.h"
Expand All @@ -25,6 +26,7 @@ void RecipientBlockAckProcedure::processReceivedBlockAckReq(Packet *blockAckPack
if (ackPolicy->isBlockAckNeeded(basicBlockAckReq, agreement)) {
auto blockAck = buildBlockAck(basicBlockAckReq, agreement);
auto duration = ackPolicy->computeBasicBlockAckDurationField(blockAckPacketReq, basicBlockAckReq);
duration = normalizeIeee80211Duration(duration);
blockAck->setDurationField(duration);
auto blockAckPacket = new Packet("BasicBlockAck", blockAck);
EV_DEBUG << "Duration for " << blockAckPacket->getName() << " is set to " << duration << " s.\n";
Expand Down
2 changes: 2 additions & 0 deletions src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//


#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.h"

#include "inet/common/ModuleAccess.h"
Expand Down Expand Up @@ -194,6 +195,7 @@ void Dcf::transmitFrame(Packet *packet, simtime_t ifs)
auto pendingPacket = channelAccess->getInProgressFrames()->getPendingFrameFor(packet);
auto duration = originatorProtectionMechanism->computeDurationField(packet, header, pendingPacket, pendingPacket == nullptr ? nullptr : pendingPacket->peekAtFront<Ieee80211DataOrMgmtHeader>());
const auto& updatedHeader = packet->removeAtFront<Ieee80211MacHeader>();
duration = normalizeIeee80211Duration(duration);
updatedHeader->setDurationField(duration);
EV_DEBUG << "Duration for " << packet->getName() << " is set to " << duration << " s.\n";
packet->insertAtFront(updatedHeader);
Expand Down
2 changes: 2 additions & 0 deletions src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//


#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"
#include "inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.h"

#include "inet/common/ModuleAccess.h"
Expand Down Expand Up @@ -719,6 +720,7 @@ void Hcf::transmitFrame(Packet *packet, simtime_t ifs)
const auto& pendingHeader = pendingPacket == nullptr ? nullptr : pendingPacket->peekAtFront<Ieee80211DataOrMgmtHeader>();
auto duration = singleProtectionMechanism->computeDurationField(packet, header, pendingPacket, pendingHeader, txop, recipientAckPolicy);
auto header = packet->removeAtFront<Ieee80211MacHeader>();
duration = normalizeIeee80211Duration(duration);
header->setDurationField(duration);
EV_DEBUG << "Duration for " << packet->getName() << " is set to " << duration << " s.\n";
packet->insertAtFront(header);
Expand Down
3 changes: 2 additions & 1 deletion src/inet/linklayer/ieee80211/mac/recipient/CtsPolicy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


#include "inet/linklayer/ieee80211/mac/recipient/CtsPolicy.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"

#include "inet/common/ModuleAccess.h"

Expand Down Expand Up @@ -35,7 +36,7 @@ simtime_t CtsPolicy::computeCtsDuration(Packet *rtsPacket, const Ptr<const Ieee8
//
simtime_t CtsPolicy::computeCtsDurationField(Packet *rtsPacket, const Ptr<const Ieee80211RtsFrame>& rtsFrame) const
{
simtime_t duration = rtsFrame->getDurationField() - modeSet->getSifsTime() - computeCtsDuration(rtsPacket, rtsFrame);
simtime_t duration = getIeee80211OrdinaryDuration(rtsFrame->getDurationField()) - modeSet->getSifsTime() - computeCtsDuration(rtsPacket, rtsFrame);
return duration < 0 ? 0 : duration;
}

Expand Down
2 changes: 2 additions & 0 deletions src/inet/linklayer/ieee80211/mac/recipient/CtsProcedure.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//


#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"
#include "inet/linklayer/ieee80211/mac/recipient/CtsProcedure.h"

namespace inet {
Expand All @@ -18,6 +19,7 @@ void CtsProcedure::processReceivedRts(Packet *rtsPacket, const Ptr<const Ieee802
if (ctsPolicy->isCtsNeeded(rtsFrame)) {
auto ctsFrame = buildCts(rtsFrame);
auto duration = ctsPolicy->computeCtsDurationField(rtsPacket, rtsFrame);
duration = normalizeIeee80211Duration(duration);
ctsFrame->setDurationField(duration);
auto ctsPacket = new Packet("CTS", ctsFrame);
EV_DEBUG << "Duration for " << ctsPacket->getName() << " is set to " << duration << " s.\n";
Expand Down
3 changes: 2 additions & 1 deletion src/inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


#include "inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"

#include "inet/common/ModuleAccess.h"

Expand Down Expand Up @@ -36,7 +37,7 @@ simtime_t QosCtsPolicy::computeCtsDuration(Packet *rtsPacket, const Ptr<const Ie
//
simtime_t QosCtsPolicy::computeCtsDurationField(Packet *rtsPacket, const Ptr<const Ieee80211RtsFrame>& rtsFrame) const
{
simtime_t duration = rtsFrame->getDurationField() - modeSet->getSifsTime() - computeCtsDuration(rtsPacket, rtsFrame);
simtime_t duration = getIeee80211OrdinaryDuration(rtsFrame->getDurationField()) - modeSet->getSifsTime() - computeCtsDuration(rtsPacket, rtsFrame);
return duration < 0 ? 0 : duration;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


#include "inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"

#include "inet/common/ModuleAccess.h"

Expand Down Expand Up @@ -53,7 +54,7 @@ bool RecipientAckPolicy::isAckNeeded(const Ptr<const Ieee80211DataOrMgmtHeader>&
simtime_t RecipientAckPolicy::computeAckDurationField(Packet *packet, const Ptr<const Ieee80211DataOrMgmtHeader>& header) const
{
if (header->getMoreFragments()) {
auto duration = header->getDurationField() - modeSet->getSifsTime() - computeAckDuration(packet, header);
auto duration = getIeee80211OrdinaryDuration(header->getDurationField()) - modeSet->getSifsTime() - computeAckDuration(packet, header);
duration = ceil(duration, SimTime(1, SIMTIME_US));
if (duration < 0)
EV_WARN << "ACK duration field would be negative, returning 0 instead.\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//


#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"
#include "inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.h"

namespace inet {
Expand All @@ -18,6 +19,7 @@ void RecipientAckProcedure::processReceivedFrame(Packet *packet, const Ptr<const
if (ackPolicy->isAckNeeded(dataOrMgmtHeader)) {
auto ackFrame = buildAck(dataOrMgmtHeader);
auto duration = ackPolicy->computeAckDurationField(packet, dataOrMgmtHeader);
duration = normalizeIeee80211Duration(duration);
ackFrame->setDurationField(duration);
auto ackPacket = new Packet("WlanAck", ackFrame);
EV_DEBUG << "Duration for " << ackFrame->getName() << " is set to " << duration << " s.\n";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


#include "inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.h"
#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h"

#include "inet/common/ModuleAccess.h"

Expand Down Expand Up @@ -78,7 +79,7 @@ bool RecipientQosAckPolicy::isBlockAckNeeded(const Ptr<const Ieee80211BlockAckRe
//
simtime_t RecipientQosAckPolicy::computeAckDurationField(Packet *packet, const Ptr<const Ieee80211DataOrMgmtHeader>& header) const
{
simtime_t duration = header->getDurationField() - modeSet->getSifsTime() - computeAckDuration(packet, header);
simtime_t duration = getIeee80211OrdinaryDuration(header->getDurationField()) - modeSet->getSifsTime() - computeAckDuration(packet, header);
return duration < 0 ? 0 : duration;
}

Expand All @@ -91,9 +92,10 @@ simtime_t RecipientQosAckPolicy::computeAckDurationField(Packet *packet, const P
//
simtime_t RecipientQosAckPolicy::computeBasicBlockAckDurationField(Packet *packet, const Ptr<const Ieee80211BasicBlockAckReq>& basicBlockAckReq) const
{
return basicBlockAckReq->getDurationField() - modeSet->getSifsTime() - computeBasicBlockAckDuration(packet, basicBlockAckReq);
simtime_t duration = getIeee80211OrdinaryDuration(basicBlockAckReq->getDurationField()) - modeSet->getSifsTime() - computeBasicBlockAckDuration(packet, basicBlockAckReq);
// IEEE Std 802.11-2024, 9.2.5.1: a negative calculated Duration/ID is inserted as zero.
return duration < 0 ? 0 : duration;
}

} /* namespace ieee80211 */
} /* namespace inet */

50 changes: 50 additions & 0 deletions tests/unit/Ieee80211BlockAckDuration_1.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
%description:
A Basic BlockAck response must clamp a negative calculated Duration to zero
before it reaches the model header or serializer (IEEE 802.11-2024, 9.2.5.1).

%includes:
#include "inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.h"
using namespace inet;
using namespace inet::ieee80211;
using namespace inet::physicallayer;

%global:
class FixedBlockAckRate : public IQosRateSelection
{
public:
const IIeee80211Mode *mode = Ieee80211ModeSet::getModeSet("a")->getMode(Mbps(24));
virtual const IIeee80211Mode *computeResponseBlockAckFrameMode(Packet *, const Ptr<const Ieee80211BlockAckReq>&) override { return mode; }
virtual const IIeee80211Mode *computeResponseCtsFrameMode(Packet *, const Ptr<const Ieee80211RtsFrame>&) override { throw cRuntimeError("unexpected CTS lookup"); }
virtual const IIeee80211Mode *computeResponseAckFrameMode(Packet *, const Ptr<const Ieee80211DataOrMgmtHeader>&) override { throw cRuntimeError("unexpected ACK lookup"); }
virtual const IIeee80211Mode *computeMode(Packet *, const Ptr<const Ieee80211MacHeader>&, TxopProcedure *) override { throw cRuntimeError("unexpected data lookup"); }
};
class BlockAckDurationPolicy : public RecipientQosAckPolicy
{
public:
void configure(IQosRateSelection *selection) {
rateSelection = selection;
modeSet = const_cast<Ieee80211ModeSet *>(Ieee80211ModeSet::getModeSet("a"));
}
};

%activity:
FixedBlockAckRate rate;
BlockAckDurationPolicy policy;
policy.configure(&rate);
Packet packet("BAR");
const auto elapsed = Ieee80211ModeSet::getModeSet("a")->getSifsTime() + rate.mode->getDuration(LENGTH_BASIC_BLOCKACK);
for (auto duration : {SIMTIME_ZERO, elapsed - SimTime(1, SIMTIME_US), elapsed, elapsed + SimTime(37, SIMTIME_US)}) {
auto bar = makeShared<Ieee80211BasicBlockAckReq>();
bar->setDurationField(duration);
auto actual = policy.computeBasicBlockAckDurationField(&packet, bar);
ASSERT(actual == (duration <= elapsed ? SIMTIME_ZERO : SimTime(37, SIMTIME_US)));
}
for (int special : {32768, 65535}) {
auto bar = makeShared<Ieee80211BasicBlockAckReq>();
bar->setDurationField(SimTime(special, SIMTIME_US));
ASSERT(policy.computeBasicBlockAckDurationField(&packet, bar) == SIMTIME_ZERO);
}
EV << "BlockAck duration is zero for insufficient reservations and preserves positive residuals.\n";

%contains: stdout
BlockAck duration is zero for insufficient reservations and preserves positive residuals.
Loading