diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Duration.h b/src/inet/linklayer/ieee80211/mac/Ieee80211Duration.h new file mode 100644 index 00000000000..7496df17bfa --- /dev/null +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Duration.h @@ -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(normalizeIeee80211Duration(duration).inUnit(SIMTIME_US)); +} + +} // namespace ieee80211 +} // namespace inet + +#endif diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc b/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc index 2a482cded2f..c33160c40bd 100644 --- a/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc @@ -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" @@ -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(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()); @@ -222,7 +223,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c case ST_ACTION: case ST_NOACKACTION: { auto mgmtHeader = dynamicPtrCast(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()); @@ -298,7 +299,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c } case ST_RTS: { auto rtsFrame = dynamicPtrCast(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()) @@ -308,7 +309,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c } case ST_CTS: { auto ctsFrame = dynamicPtrCast(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", @@ -317,7 +318,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c } case ST_ACK: { auto ackFrame = dynamicPtrCast(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", @@ -326,7 +327,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c } case ST_BLOCKACK_REQ: { auto blockAckReq = dynamicPtrCast(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(); @@ -362,7 +363,7 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c } case ST_BLOCKACK: { auto blockAck = dynamicPtrCast(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(); diff --git a/src/inet/linklayer/ieee80211/mac/Rx.cc b/src/inet/linklayer/ieee80211/mac/Rx.cc index 19b9da0889c..0627ec68f5d 100644 --- a/src/inet/linklayer/ieee80211/mac/Rx.cc +++ b/src/inet/linklayer/ieee80211/mac/Rx.cc @@ -99,8 +99,11 @@ bool Rx::lowerFrameReceived(Packet *packet) if (isFrameOk) { EV_INFO << "Received frame from PHY: " << packet << endl; const auto& header = packet->peekAtFront(); - 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 { diff --git a/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc b/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc index bd4b3e82321..6681e7ae443 100644 --- a/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc +++ b/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc @@ -69,6 +69,7 @@ std::vector *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); diff --git a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc index ab7cb050e5f..74d63f2c446 100644 --- a/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc +++ b/src/inet/linklayer/ieee80211/mac/blockack/RecipientBlockAckProcedure.cc @@ -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" @@ -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"; diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc index ddb1cf9aa65..1452ed3ca71 100644 --- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc +++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.cc @@ -5,6 +5,7 @@ // +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/linklayer/ieee80211/mac/coordinationfunction/Dcf.h" #include "inet/common/ModuleAccess.h" @@ -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()); const auto& updatedHeader = packet->removeAtFront(); + duration = normalizeIeee80211Duration(duration); updatedHeader->setDurationField(duration); EV_DEBUG << "Duration for " << packet->getName() << " is set to " << duration << " s.\n"; packet->insertAtFront(updatedHeader); diff --git a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc index e64ce4db852..9292a1f17ac 100644 --- a/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc +++ b/src/inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.cc @@ -5,6 +5,7 @@ // +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/linklayer/ieee80211/mac/coordinationfunction/Hcf.h" #include "inet/common/ModuleAccess.h" @@ -719,6 +720,7 @@ void Hcf::transmitFrame(Packet *packet, simtime_t ifs) const auto& pendingHeader = pendingPacket == nullptr ? nullptr : pendingPacket->peekAtFront(); auto duration = singleProtectionMechanism->computeDurationField(packet, header, pendingPacket, pendingHeader, txop, recipientAckPolicy); auto header = packet->removeAtFront(); + duration = normalizeIeee80211Duration(duration); header->setDurationField(duration); EV_DEBUG << "Duration for " << packet->getName() << " is set to " << duration << " s.\n"; packet->insertAtFront(header); diff --git a/src/inet/linklayer/ieee80211/mac/recipient/CtsPolicy.cc b/src/inet/linklayer/ieee80211/mac/recipient/CtsPolicy.cc index 17edca5ffea..d90db6f5d1e 100644 --- a/src/inet/linklayer/ieee80211/mac/recipient/CtsPolicy.cc +++ b/src/inet/linklayer/ieee80211/mac/recipient/CtsPolicy.cc @@ -6,6 +6,7 @@ #include "inet/linklayer/ieee80211/mac/recipient/CtsPolicy.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/common/ModuleAccess.h" @@ -35,7 +36,7 @@ simtime_t CtsPolicy::computeCtsDuration(Packet *rtsPacket, const Ptr& 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; } diff --git a/src/inet/linklayer/ieee80211/mac/recipient/CtsProcedure.cc b/src/inet/linklayer/ieee80211/mac/recipient/CtsProcedure.cc index dd0dad9936f..0cb1c24fdee 100644 --- a/src/inet/linklayer/ieee80211/mac/recipient/CtsProcedure.cc +++ b/src/inet/linklayer/ieee80211/mac/recipient/CtsProcedure.cc @@ -5,6 +5,7 @@ // +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/linklayer/ieee80211/mac/recipient/CtsProcedure.h" namespace inet { @@ -18,6 +19,7 @@ void CtsProcedure::processReceivedRts(Packet *rtsPacket, const PtrisCtsNeeded(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"; diff --git a/src/inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.cc b/src/inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.cc index afd8af57f9d..8fe29cce3ea 100644 --- a/src/inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.cc +++ b/src/inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.cc @@ -6,6 +6,7 @@ #include "inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/common/ModuleAccess.h" @@ -36,7 +37,7 @@ simtime_t QosCtsPolicy::computeCtsDuration(Packet *rtsPacket, const Ptr& 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; } diff --git a/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.cc b/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.cc index af8077331bb..59e0e201c7f 100644 --- a/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.cc +++ b/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.cc @@ -6,6 +6,7 @@ #include "inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/common/ModuleAccess.h" @@ -53,7 +54,7 @@ bool RecipientAckPolicy::isAckNeeded(const Ptr& simtime_t RecipientAckPolicy::computeAckDurationField(Packet *packet, const Ptr& 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"; diff --git a/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.cc b/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.cc index 064934a2c1c..fc4975eb49e 100644 --- a/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.cc +++ b/src/inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.cc @@ -5,6 +5,7 @@ // +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.h" namespace inet { @@ -18,6 +19,7 @@ void RecipientAckProcedure::processReceivedFrame(Packet *packet, const PtrisAckNeeded(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"; diff --git a/src/inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.cc b/src/inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.cc index 29f793f0110..3a80fb3fa42 100644 --- a/src/inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.cc +++ b/src/inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.cc @@ -6,6 +6,7 @@ #include "inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" #include "inet/common/ModuleAccess.h" @@ -78,7 +79,7 @@ bool RecipientQosAckPolicy::isBlockAckNeeded(const Ptr& 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; } @@ -91,9 +92,10 @@ simtime_t RecipientQosAckPolicy::computeAckDurationField(Packet *packet, const P // simtime_t RecipientQosAckPolicy::computeBasicBlockAckDurationField(Packet *packet, const Ptr& 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 */ - diff --git a/tests/unit/Ieee80211BlockAckDuration_1.test b/tests/unit/Ieee80211BlockAckDuration_1.test new file mode 100644 index 00000000000..cfddc72de71 --- /dev/null +++ b/tests/unit/Ieee80211BlockAckDuration_1.test @@ -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&) override { return mode; } + virtual const IIeee80211Mode *computeResponseCtsFrameMode(Packet *, const Ptr&) override { throw cRuntimeError("unexpected CTS lookup"); } + virtual const IIeee80211Mode *computeResponseAckFrameMode(Packet *, const Ptr&) override { throw cRuntimeError("unexpected ACK lookup"); } + virtual const IIeee80211Mode *computeMode(Packet *, const Ptr&, TxopProcedure *) override { throw cRuntimeError("unexpected data lookup"); } +}; +class BlockAckDurationPolicy : public RecipientQosAckPolicy +{ + public: + void configure(IQosRateSelection *selection) { + rateSelection = selection; + modeSet = const_cast(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(); + 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(); + 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. diff --git a/tests/unit/Ieee80211DurationRounding_1.test b/tests/unit/Ieee80211DurationRounding_1.test new file mode 100644 index 00000000000..2761a19b91b --- /dev/null +++ b/tests/unit/Ieee80211DurationRounding_1.test @@ -0,0 +1,86 @@ +%description: +Ordinary Duration/ID fields round fractional microseconds upward in every +supported MAC header family (IEEE 802.11-2024, 9.2.5.1). + +%includes: +#include "inet/common/packet/Packet.h" +#include "inet/common/packet/chunk/BytesChunk.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Duration.h" +#include "inet/linklayer/ieee80211/mac/Rx.h" +using namespace inet; +using namespace inet::ieee80211; + +%global: +class TestRx : public Rx { + public: + int updates = 0; + virtual bool isFcsOk(Packet *) const override { return true; } + protected: + virtual void setOrExtendNav(simtime_t) override { updates++; } +}; +static Ptr frame(int family) +{ + switch (family) { + case 0: return makeShared(); + case 1: { + auto header = makeShared(); + header->setType(ST_AUTHENTICATION); + return header; + } + case 2: return makeShared(); + case 3: return makeShared(); + case 4: return makeShared(); + case 5: return makeShared(); + default: { + auto header = makeShared(); + for (int i = 0; i < 64; i++) + for (int bit = 0; bit < 16; bit++) + header->getBlockAckBitmapForUpdate(i).setBit(bit, false); + return header; + } + } +} + +%activity: +ASSERT(normalizeIeee80211Duration(SimTime(-1, SIMTIME_US)) == SIMTIME_ZERO); +ASSERT(normalizeIeee80211Duration(SimTime(44800, SIMTIME_NS)) == SimTime(45, SIMTIME_US)); +ASSERT(normalizeIeee80211Duration(SimTime(32766800, SIMTIME_NS)) == SimTime(32767, SIMTIME_US)); +for (auto invalid : {SimTime(-1, SIMTIME_US), SimTime(32767100, SIMTIME_NS)}) { + bool rejected = false; + try { encodeIeee80211Duration(invalid); } + catch (const cRuntimeError&) { rejected = true; } + ASSERT(rejected); +} +for (int family = 0; family < 7; family++) { + for (int nanoseconds : {0, 1, 44000, 44800, 32766800}) { + auto header = frame(family); + if (auto data = dynamicPtrCast(header)) + data->setSequenceNumber(SequenceNumberCyclic(0)); + if (auto bar = dynamicPtrCast(header)) + bar->setStartingSequenceNumber(SequenceNumberCyclic(0)); + if (auto ba = dynamicPtrCast(header)) + ba->setStartingSequenceNumber(SequenceNumberCyclic(0)); + header->setDurationField(SimTime(nanoseconds, SIMTIME_NS)); + Packet packet("duration", header); + const auto bytes = packet.peekAllAsBytes()->getBytes(); + ASSERT(bytes.size() >= 4); + int encoded = bytes[2] | (bytes[3] << 8); + ASSERT(encoded == (nanoseconds + 999) / 1000); + } +} +TestRx rx; +for (int duration : {-1, 0, 32767, 32768, 65535}) { + auto header = makeShared(); + header->setReceiverAddress(MacAddress("02:00:00:00:00:12")); + header->setDurationField(SimTime(duration, SIMTIME_US)); + auto packet = new Packet("nav", header); + int before = rx.updates; + ASSERT(rx.lowerFrameReceived(packet)); + ASSERT(rx.updates - before == (duration >= 0 && duration <= 32767 ? 1 : 0)); + delete packet; +} +EV << "All seven ordinary MAC Duration fields round upward.\n"; + +%contains: stdout +All seven ordinary MAC Duration fields round upward. diff --git a/tests/unit/Ieee80211MsduAggregation_1.test b/tests/unit/Ieee80211MsduAggregation_1.test index c6b142c9e29..7c78c00971b 100644 --- a/tests/unit/Ieee80211MsduAggregation_1.test +++ b/tests/unit/Ieee80211MsduAggregation_1.test @@ -67,6 +67,8 @@ for (bool toDS : {false, true}) { // OriginatorQosMacDataService assigns the sequence after aggregation, before transmission. auto sequencedHeader = aggregate->removeAtFront(); sequenceNumbers.assignSequenceNumber(sequencedHeader); + // The MAC assigns Duration before a frame is serialized for transmission. + sequencedHeader->setDurationField(SIMTIME_ZERO); aggregate->insertAtFront(sequencedHeader); auto outer = aggregate->peekAtFront(); ASSERT(outer->getReceiverAddress() == ra); diff --git a/tests/unit/Ieee80211OnWireBitCompliance_1.test b/tests/unit/Ieee80211OnWireBitCompliance_1.test index afebe6f9f27..33742bd38ec 100644 --- a/tests/unit/Ieee80211OnWireBitCompliance_1.test +++ b/tests/unit/Ieee80211OnWireBitCompliance_1.test @@ -21,6 +21,7 @@ for (int ds = 0; ds < 4; ds++) { for (int policy = 0; policy < 4; policy++) { auto header = makeShared(); header->setType(ST_DATA_WITH_QOS); + header->setDurationField(SIMTIME_ZERO); header->setSequenceNumber(SequenceNumberCyclic(0)); header->setToDS(ds & 1); header->setFromDS(ds & 2); diff --git a/tests/unit/Ieee80211ResponseDuration_1.test b/tests/unit/Ieee80211ResponseDuration_1.test new file mode 100644 index 00000000000..3d1225908e8 --- /dev/null +++ b/tests/unit/Ieee80211ResponseDuration_1.test @@ -0,0 +1,60 @@ +%description: +Reserved/special Duration/ID encodings do not become ordinary ACK or CTS +reservations; valid ordinary durations preserve their residual airtime. + +%includes: +#include "inet/linklayer/ieee80211/mac/recipient/CtsPolicy.h" +#include "inet/linklayer/ieee80211/mac/rateselection/RateSelection.h" +#include "inet/linklayer/ieee80211/mac/rateselection/QosRateSelection.h" +#include "inet/linklayer/ieee80211/mac/recipient/QosCtsPolicy.h" +#include "inet/linklayer/ieee80211/mac/recipient/RecipientAckPolicy.h" +#include "inet/linklayer/ieee80211/mac/recipient/RecipientQosAckPolicy.h" +using namespace inet; +using namespace inet::ieee80211; +using namespace inet::physicallayer; + +%global: +template class TestCts : public T { + public: + TestCts() { this->modeSet = const_cast(Ieee80211ModeSet::getModeSet("a")); } + protected: + virtual simtime_t computeCtsDuration(Packet *, const Ptr&) const override { return SimTime(10, SIMTIME_US); } +}; +template class FixedAckRate : public T { + public: + virtual const IIeee80211Mode *computeResponseAckFrameMode(Packet *, const Ptr&) override { + return Ieee80211ModeSet::getModeSet("a")->getMode(Mbps(24)); + } +}; +template class TestAck : public T { + protected: + FixedAckRate rate; + public: + TestAck() { + this->modeSet = const_cast(Ieee80211ModeSet::getModeSet("a")); + this->rateSelection = &rate; + } +}; + +%activity: +TestCts dcfCts; +TestCts hcfCts; +TestAck dcfAck; +TestAck hcfAck; +Packet packet("request"); +for (int duration : {-1, 0, 200, 32768, 65535}) { + auto rts = makeShared(); + rts->setDurationField(SimTime(duration, SIMTIME_US)); + auto data = makeShared(); + data->setMoreFragments(true); + data->setDurationField(SimTime(duration, SIMTIME_US)); + auto expected = duration == 200 ? SimTime(174, SIMTIME_US) : SIMTIME_ZERO; + ASSERT(dcfCts.computeCtsDurationField(&packet, rts) == expected); + ASSERT(hcfCts.computeCtsDurationField(&packet, rts) == expected); + ASSERT(dcfAck.computeAckDurationField(&packet, data) == (duration == 200 ? SimTime(156, SIMTIME_US) : SIMTIME_ZERO)); + ASSERT(hcfAck.computeAckDurationField(&packet, data) == (duration == 200 ? SimTime(156, SIMTIME_US) : SIMTIME_ZERO)); +} +EV << "ACK and CTS policies ignore non-duration encodings.\n"; + +%contains: stdout +ACK and CTS policies ignore non-duration encodings.