diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg b/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg index 033ee7141be..ebae6a54dac 100644 --- a/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211Frame.msg @@ -312,7 +312,7 @@ class Ieee80211AddbaResponse extends Ieee80211ActionFrame // class Ieee80211Delba extends Ieee80211ActionFrame { - chunkLength = LENGTH_DELBA; + chunkLength = LENGTH_DELBA - B(4); // FCS is carried by Ieee80211MacTrailer category = 3; // Category field is set to 3 (representing DELBA). (1 byte) blockAckAction = 2; // Block Ack Action field is set to 2 (representing DELBA). (1 byte) diff --git a/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc b/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc index fab687ca125..6dfab5ad552 100644 --- a/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc +++ b/src/inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.cc @@ -7,6 +7,7 @@ #include "inet/linklayer/ieee80211/mac/Ieee80211MacHeaderSerializer.h" +#include "inet/common/checksum/Checksum.h" #include "inet/common/packet/serializer/ChunkSerializerRegistry.h" namespace inet { @@ -64,6 +65,19 @@ uint16_t packSequenceControl(uint8_t fragmentNumber, uint16_t sequenceNumber) return (fragmentNumber & 0xF) | ((sequenceNumber & 0xFFF) << 4); } +uint16_t packBlockAckParameters(bool aMsduSupported, bool blockAckPolicy, uint8_t tid, uint16_t bufferSize) +{ + // IEEE Std 802.11-2024, 9.4.1.13, Figure 9-151. + return aMsduSupported | (blockAckPolicy << 1) | ((tid & 0xF) << 2) | ((bufferSize & 0x3FF) << 6); +} + +uint8_t computeMpduDelimiterCrc(uint16_t delimiter) +{ + // IEEE Std 802.11-2024, 9.7.2: x^8 + x^2 + x + 1, complemented remainder. + const uint8_t bytes[] = {uint8_t(delimiter), uint8_t(delimiter >> 8)}; + return generic_crc8(bytes, sizeof(bytes), 0x07, 0xFF, true, true, 0xFF); +} + void writeSequenceControl(MemoryOutputStream& stream, uint8_t fragmentNumber, uint16_t sequenceNumber) { stream.writeUint16Le(packSequenceControl(fragmentNumber, sequenceNumber)); @@ -126,24 +140,25 @@ const Ptr Ieee80211MsduSubframeHeaderSerializer::deserializeFields(Memory void Ieee80211MpduSubframeHeaderSerializer::serializeFields(MemoryOutputStream& stream, const Ptr& chunk) const { auto mpduSubframe = dynamicPtrCast(chunk); - stream.writeUint4(0); - stream.writeUint4(mpduSubframe->getLength() >> 8); - stream.writeUint8(mpduSubframe->getLength() & 0xFF); - stream.writeByte(0); + // IEEE Std 802.11-2024, 9.7.1, Figures 9-1329/9-1330: HT length occupies B4-B15. + int length = mpduSubframe->getLength(); + if (length < 0 || length > 0xFFF) + throw cRuntimeError("HT MPDU delimiter length is outside the 12-bit range: %d", length); + uint16_t delimiter = length << 4; + stream.writeUint16Le(delimiter); + stream.writeByte(computeMpduDelimiterCrc(delimiter)); stream.writeByte(0x4E); } const Ptr Ieee80211MpduSubframeHeaderSerializer::deserializeFields(MemoryInputStream& stream, const std::type_info&) const { auto mpduSubframe = makeShared(); - stream.readUint4(); - // IEEE Std 802.11-2024, 9.7.1, Table 9-659 and Figure 9-1330: - // in an HT PPDU, MPDU Length Low is 12 bits and MPDU Length High is reserved. - int length = stream.readUint4() << 8; - length |= stream.readUint8(); - mpduSubframe->setLength(length); - stream.readByte(); - stream.readByte(); + auto delimiter = stream.readUint16Le(); + mpduSubframe->setLength(delimiter >> 4); + auto crc = stream.readByte(); + auto signature = stream.readByte(); + if (crc != computeMpduDelimiterCrc(delimiter) || signature != 0x4E) + mpduSubframe->markIncorrect(); return mpduSubframe; } @@ -195,13 +210,11 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c auto addbaRequest = dynamicPtrCast(chunk); stream.writeByte(addbaRequest->getBlockAckAction()); stream.writeByte(addbaRequest->getDialogToken()); - stream.writeBit(addbaRequest->getAMsduSupported()); - stream.writeBit(addbaRequest->getBlockAckPolicy()); - stream.writeUint4(addbaRequest->getTid()); - stream.writeNBitsOfUint64Be(addbaRequest->getBufferSize(), 10); - stream.writeUint16Be(addbaRequest->getBlockAckTimeoutValue().inUnit(SIMTIME_US) / 1024); - stream.writeUint4(addbaRequest->get_fragmentNumber()); - stream.writeNBitsOfUint64Be(addbaRequest->getStartingSequenceNumber().get(), 12); + stream.writeUint16Le(packBlockAckParameters(addbaRequest->getAMsduSupported(), + addbaRequest->getBlockAckPolicy(), addbaRequest->getTid(), addbaRequest->getBufferSize())); + stream.writeUint16Le(addbaRequest->getBlockAckTimeoutValue().inUnit(SIMTIME_US) / 1024); + // IEEE Std 802.11-2024, 9.6.4.2: Fragment Number is zero. + writeSequenceControl(stream, 0, addbaRequest->getStartingSequenceNumber().get()); ASSERT(stream.getLength() - startPos == addbaRequest->getChunkLength()); break; } @@ -209,22 +222,20 @@ void Ieee80211MacHeaderSerializer::serializeFields(MemoryOutputStream& stream, c auto addbaResponse = dynamicPtrCast(chunk); stream.writeByte(addbaResponse->getBlockAckAction()); stream.writeByte(addbaResponse->getDialogToken()); - stream.writeUint16Be(addbaResponse->getStatusCode()); - stream.writeBit(addbaResponse->getAMsduSupported()); - stream.writeBit(addbaResponse->getBlockAckPolicy()); - stream.writeUint4(addbaResponse->getTid()); - stream.writeNBitsOfUint64Be(addbaResponse->getBufferSize(), 10); - stream.writeUint16Be(addbaResponse->getBlockAckTimeoutValue().inUnit(SIMTIME_US) / 1024); + stream.writeUint16Le(addbaResponse->getStatusCode()); + stream.writeUint16Le(packBlockAckParameters(addbaResponse->getAMsduSupported(), + addbaResponse->getBlockAckPolicy(), addbaResponse->getTid(), addbaResponse->getBufferSize())); + stream.writeUint16Le(addbaResponse->getBlockAckTimeoutValue().inUnit(SIMTIME_US) / 1024); ASSERT(stream.getLength() - startPos == addbaResponse->getChunkLength()); break; } case 2: { auto delba = dynamicPtrCast(chunk); stream.writeByte(delba->getBlockAckAction()); - stream.writeNBitsOfUint64Be(delba->getReserved(), 11); - stream.writeBit(delba->getInitiator()); - stream.writeUint4(delba->getTid()); - stream.writeUint16Be(delba->getReasonCode()); + // IEEE Std 802.11-2024, 9.4.1.16, Figure 9-154. + stream.writeUint16Le((delba->getReserved() & 0x7FF) | + (delba->getInitiator() << 11) | ((delba->getTid() & 0xF) << 12)); + stream.writeUint16Le(delba->getReasonCode()); ASSERT(stream.getLength() - startPos == delba->getChunkLength()); break; } @@ -437,13 +448,15 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre copyActionFrameFields(addbaRequest, actionFrame); addbaRequest->setBlockAckAction(blockAckAction); addbaRequest->setDialogToken(stream.readByte()); - addbaRequest->setAMsduSupported(stream.readBit()); - addbaRequest->setBlockAckPolicy(stream.readBit()); - addbaRequest->setTid(stream.readUint4()); - addbaRequest->setBufferSize(stream.readNBitsToUint64Be(10)); - addbaRequest->setBlockAckTimeoutValue(SimTime(stream.readUint16Be() * 1024, SIMTIME_US)); - addbaRequest->set_fragmentNumber(stream.readUint4()); - addbaRequest->setStartingSequenceNumber(SequenceNumberCyclic(stream.readNBitsToUint64Be(12))); + auto parameters = stream.readUint16Le(); + addbaRequest->setAMsduSupported(parameters & 1); + addbaRequest->setBlockAckPolicy(parameters & 2); + addbaRequest->setTid((parameters >> 2) & 0xF); + addbaRequest->setBufferSize(parameters >> 6); + addbaRequest->setBlockAckTimeoutValue(SimTime(stream.readUint16Le() * 1024, SIMTIME_US)); + readSequenceControl(stream, fragmentNumber, sequenceNumber); + addbaRequest->set_fragmentNumber(fragmentNumber); + addbaRequest->setStartingSequenceNumber(sequenceNumber); return addbaRequest; } case 1: { @@ -452,22 +465,25 @@ const Ptr Ieee80211MacHeaderSerializer::deserializeFields(MemoryInputStre copyActionFrameFields(addbaResponse, actionFrame); addbaResponse->setBlockAckAction(blockAckAction); addbaResponse->setDialogToken(stream.readByte()); - addbaResponse->setStatusCode(stream.readUint16Be()); - addbaResponse->setAMsduSupported(stream.readBit()); - addbaResponse->setBlockAckPolicy(stream.readBit()); - addbaResponse->setTid(stream.readUint4()); - addbaResponse->setBufferSize(stream.readNBitsToUint64Be(10)); - addbaResponse->setBlockAckTimeoutValue(SimTime(stream.readUint16Be() * 1024, SIMTIME_US)); + addbaResponse->setStatusCode(stream.readUint16Le()); + auto parameters = stream.readUint16Le(); + addbaResponse->setAMsduSupported(parameters & 1); + addbaResponse->setBlockAckPolicy(parameters & 2); + addbaResponse->setTid((parameters >> 2) & 0xF); + addbaResponse->setBufferSize(parameters >> 6); + addbaResponse->setBlockAckTimeoutValue(SimTime(stream.readUint16Le() * 1024, SIMTIME_US)); return addbaResponse; } case 2: { auto delba = makeShared(); copyBasicFields(delba, macHeader); + copyActionFrameFields(delba, actionFrame); delba->setBlockAckAction(blockAckAction); - delba->setReserved(stream.readNBitsToUint64Be(11)); - delba->setInitiator(stream.readBit()); - delba->setTid(stream.readUint4()); - delba->setReasonCode(stream.readUint16Be()); + auto parameters = stream.readUint16Le(); + delba->setReserved(parameters & 0x7FF); + delba->setInitiator(parameters & 0x800); + delba->setTid(parameters >> 12); + delba->setReasonCode(stream.readUint16Le()); return delba; } default: diff --git a/src/inet/linklayer/ieee80211/mac/aggregation/MsduAggregation.cc b/src/inet/linklayer/ieee80211/mac/aggregation/MsduAggregation.cc index 8303a81c0ea..4f2a4dc1f99 100644 --- a/src/inet/linklayer/ieee80211/mac/aggregation/MsduAggregation.cc +++ b/src/inet/linklayer/ieee80211/mac/aggregation/MsduAggregation.cc @@ -84,9 +84,24 @@ Packet *MsduAggregation::aggregateFrames(std::vector *frames) amsduHeader->setFromDS(fromDS); amsduHeader->setAMsduPresent(true); amsduHeader->setReceiverAddress(ra); + // IEEE Std 802.11-2024, 9.3.2.2.1: aggregation preserves the common RA and TA. + amsduHeader->setTransmitterAddress(firstHeader->getTransmitterAddress()); amsduHeader->setTid(tid); amsduHeader->addChunkLength(QOSCONTROL_PART_LENGTH); - // TODO set addr3 and addr4 according to fromDS and toDS. + // IEEE Std 802.11-2024, 9.3.2.1.1, Figure 9-119. + if (toDS && fromDS) + amsduHeader->addChunkLength(B(6)); + // IEEE Std 802.11-2024, 9.3.2.1.2, Table 9-60: Basic A-MSDUs carry the + // BSSID in Address3 and, when present, Address4. In the AP-to-AP case, + // the BSSID is the address of the transmitting AP. + if (fromDS) + amsduHeader->setAddress3(firstHeader->getTransmitterAddress()); + else if (toDS) + amsduHeader->setAddress3(ra); + else + amsduHeader->setAddress3(firstHeader->getAddress3()); + if (toDS && fromDS) + amsduHeader->setAddress4(firstHeader->getTransmitterAddress()); aggregatedFrame->insertAtFront(amsduHeader); aggregatedFrame->insertAtBack(makeShared()); aggregatedFrame->setName(aggregatedName.c_str()); diff --git a/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc b/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc index 5e4cf176158..bd4b3e82321 100644 --- a/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc +++ b/src/inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.cc @@ -19,6 +19,7 @@ void MsduDeaggregation::setExplodedFrameAddress(const Ptr& if (fromDS == 0 && toDS == 0) { // STA to STA header->setTransmitterAddress(aMsduHeader->getTransmitterAddress()); header->setReceiverAddress(aMsduHeader->getReceiverAddress()); + header->setAddress3(aMsduHeader->getAddress3()); } else if (fromDS == 1 && toDS == 0) { // AP to STA header->setTransmitterAddress(aMsduHeader->getTransmitterAddress()); @@ -64,6 +65,9 @@ std::vector *MsduDeaggregation::deaggregateFrame(Packet *aggregatedFra header->addChunkLength(QOSCONTROL_PART_LENGTH); header->setToDS(amsduHeader->getToDS()); header->setFromDS(amsduHeader->getFromDS()); + // IEEE Std 802.11-2024, 9.3.2.1.1, Figure 9-119: Address4 adds six octets. + if (header->getToDS() && header->getFromDS()) + header->addChunkLength(B(6)); header->setTid(tid); header->setSequenceNumber(SequenceNumberCyclic(0)); setExplodedFrameAddress(header, msduSubframeHeader, amsduHeader); diff --git a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrameSerializer.cc b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrameSerializer.cc index 09f3ffbd05b..f874908b927 100644 --- a/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrameSerializer.cc +++ b/src/inet/linklayer/ieee80211/mgmt/Ieee80211MgmtFrameSerializer.cc @@ -758,8 +758,9 @@ const Ptr Ieee80211MgmtFrameSerializer::deserializeFields(MemoryInputStre else if (typeInfo == typeid(Ieee80211BeaconFrame)) { auto frame = makeShared(); - simtime_t timetstamp; - timetstamp.setRaw(stream.readUint64Le()); // TODO store timestamp + // IEEE Std 802.11-2024, 9.4.1.10 and 11.1.3.1: unsigned TSF counter in microseconds. + // The model does not retain TSF; no conversion to simulation-resolution ticks is needed. + stream.readUint64Le(); frame->setBeaconInterval(SimTime((int64_t)stream.readUint16Le() * 1024, SIMTIME_US)); stream.readUint16Le(); // Capability @@ -775,8 +776,9 @@ const Ptr Ieee80211MgmtFrameSerializer::deserializeFields(MemoryInputStre else if (typeInfo == typeid(Ieee80211ProbeResponseFrame)) { auto frame = makeShared(); - simtime_t timestamp; - timestamp.setRaw(stream.readUint64Le()); // TODO store timestamp + // IEEE Std 802.11-2024, 9.4.1.10 and 11.1.3.1: unsigned TSF counter in microseconds. + // The model does not retain TSF; no conversion to simulation-resolution ticks is needed. + stream.readUint64Le(); frame->setBeaconInterval(SimTime((int64_t)stream.readUint16Le() * 1024, SIMTIME_US)); stream.readUint16Le(); diff --git a/tests/fingerprint/examples.csv b/tests/fingerprint/examples.csv index 696ba2cb33b..11192d39c06 100644 --- a/tests/fingerprint/examples.csv +++ b/tests/fingerprint/examples.csv @@ -7,7 +7,7 @@ # /examples/adhoc/ieee80211/, -f omnetpp.ini -c Ping2 -r 0, 100s, 0000-0000/tplx;0000-0000/~tNl;0000-0000/~tND, ERROR, wireless adhoc # [Config Ping2] # interactive config, needed a *.numHosts parameter /examples/adhoc/qos/, -f omnetpp.ini -c MacNonQos -r 0, 10s, 5749-0281/tplx;f38a-cb93/~tNl;fd9b-683f/~tND;0eb8-3e3b/tyf, PASS, wireless adhoc Ipv4 /examples/adhoc/qos/, -f omnetpp.ini -c MacQos -r 0, 10s, 1486-cbae/tplx;521b-977a/~tNl;91c2-9aa9/~tND;2c1e-66e5/tyf, PASS, wireless adhoc Ipv4 -/examples/adhoc/qos/, -f omnetpp.ini -c MacQos -r 1, 10s, 783f-d09a/tplx;13d1-ec15/~tNl;5aaf-cc7c/~tND;a587-3a4d/tyf, PASS, wireless adhoc Ipv4 +/examples/adhoc/qos/, -f omnetpp.ini -c MacQos -r 1, 10s, 783f-d09a/tplx;13d1-ec15/~tNl;a50d-9ad8/~tND;a587-3a4d/tyf, PASS, wireless adhoc Ipv4 /examples/adhoc/qos/, -f omnetpp.ini -c Fragmentation, 10s, 9c66-e6f0/tplx;cc9e-d1a6/~tNl;53c8-e1b0/~tND;33b7-00f9/tyf, PASS, wireless adhoc Ipv4 /examples/adhoc/qos/, -f omnetpp.ini -c MsduAggregation, 10s, 85bd-85c6/tplx;22f6-085d/~tNl;dc0d-4e19/~tND;df04-18f7/tyf, PASS, wireless adhoc Ipv4 @@ -655,10 +655,10 @@ /examples/wireless/power/, -f omnetpp.ini -c General -r 0, 100s, 6fae-d558/tplx;b8ea-b2fc/~tNl;5ea8-2cea/~tND, PASS, wireless Ipv4 /examples/wireless/qos/, -f omnetpp.ini -c MacNonQos -r 0, 10s, f252-8a4b/tplx;481d-4747/~tNl;5962-e45d/~tND;6097-a429/tyf, PASS, wireless Ipv4 -/examples/wireless/qos/, -f omnetpp.ini -c MacQos -r 0, 10s, 31b0-6212/tplx;82ec-9fde/~tNl;540d-c0a4/~tND;9f3c-4512/tyf, PASS, wireless Ipv4 +/examples/wireless/qos/, -f omnetpp.ini -c MacQos -r 0, 10s, 31b0-6212/tplx;82ec-9fde/~tNl;9839-8fb0/~tND;9f3c-4512/tyf, PASS, wireless Ipv4 /examples/wireless/qos/, -f omnetpp.ini -c MacQosWithoutAggregation -r 0, 10s, acd6-0108/tplx;b339-294a/~tNl;75d4-11f9/~tND;a4c3-19bf/tyf, PASS, wireless Ipv4 -/examples/wireless/qos/, -f omnetpp.ini -c MacQosWithRtsCts -r 0, 10s, 9ece-fbfb/tplx;c1af-29ff/~tNl;8fe3-d7a6/~tND;b2d6-1329/tyf, PASS, wireless Ipv4 -/examples/wireless/qos/, -f omnetpp.ini -c MacQosWithBlockAck -r 0, 10s, d094-b008/tplx;173a-e3fb/~tNl;7147-4f5b/~tND;08b5-d005/tyf, PASS, wireless Ipv4 +/examples/wireless/qos/, -f omnetpp.ini -c MacQosWithRtsCts -r 0, 10s, 9ece-fbfb/tplx;c1af-29ff/~tNl;4757-c17b/~tND;b2d6-1329/tyf, PASS, wireless Ipv4 +/examples/wireless/qos/, -f omnetpp.ini -c MacQosWithBlockAck -r 0, 10s, d094-b008/tplx;173a-e3fb/~tNl;6541-bd60/~tND;08b5-d005/tyf, PASS, wireless Ipv4 /examples/wireless/ratecontrol/, -f omnetpp.ini -c Mac -r 0, 100s, bf30-2f13/tplx;7b2f-653d/~tNl;6e1e-3b7b/~tND;19fe-8b0e/tyf, PASS, wireless diff --git a/tests/fingerprint/showcases.csv b/tests/fingerprint/showcases.csv index ef401f4edb1..1b203169953 100644 --- a/tests/fingerprint/showcases.csv +++ b/tests/fingerprint/showcases.csv @@ -199,9 +199,9 @@ /showcases/wireless/analogmodel/, -f omnetpp.ini -c Distance -r 0, 2.5s, 1e75-270e/tplx;6d7a-d84c/~tNl;b380-6cd5/~tND;5575-fd8f/tyf, PASS, wireless Ipv4 /showcases/wireless/analogmodel/, -f omnetpp.ini -c Noise -r 0, 0.1s, dd08-a63c/tplx;e167-9c84/~tNl;643d-41a6/~tND;0d1f-df73/tyf, PASS, wireless Ipv4 -/showcases/wireless/blockack/, -f omnetpp.ini -c NoFragmentation -r 0, 1s, aa2d-5d35/tplx;2094-1f2a/~tNl;1470-1e1b/~tND, PASS, wireless Ipv4 -/showcases/wireless/blockack/, -f omnetpp.ini -c Fragmentation -r 0, 1s, 7ae9-e07d/tplx;db8b-3b81/~tNl;9c41-dc97/~tND, PASS, wireless Ipv4 -/showcases/wireless/blockack/, -f omnetpp.ini -c MixedTraffic -r 0, 1s, 462d-10c7/tplx;727b-d26a/~tNl;62c4-cbc2/~tND, PASS, wireless Ipv4 +/showcases/wireless/blockack/, -f omnetpp.ini -c NoFragmentation -r 0, 1s, aa2d-5d35/tplx;2094-1f2a/~tNl;8b71-c964/~tND, PASS, wireless Ipv4 +/showcases/wireless/blockack/, -f omnetpp.ini -c Fragmentation -r 0, 1s, 7ae9-e07d/tplx;db8b-3b81/~tNl;9b1c-22eb/~tND, PASS, wireless Ipv4 +/showcases/wireless/blockack/, -f omnetpp.ini -c MixedTraffic -r 0, 1s, 462d-10c7/tplx;727b-d26a/~tNl;153d-3bdf/~tND, PASS, wireless Ipv4 /showcases/wireless/crosstalk/, -f omnetpp.ini -c CompletelyOverlappingFrequencyBands -r 0, 1s, d0d7-43e0/tplx;867a-07a4/~tNl;df78-8445/~tND;3ea3-43da/tyf, PASS, wireless Ipv4 /showcases/wireless/crosstalk/, -f omnetpp.ini -c IndependentFrequencyBandsOneRadioMediumModule -r 0, 1s, 70c6-72b6/tplx;cf96-5e4d/~tNl;3cba-ae59/~tND;d1b2-9fd4/tyf, PASS, wireless Ipv4 @@ -267,7 +267,7 @@ /showcases/wireless/fragmentation/, -f omnetpp.ini -c DCFnofrag -r 0, 1s, 52b9-628f/tplx;3fec-74a2/~tNl;6073-4582/~tND;8871-1dd1/tyf, PASS, wireless Ipv4 /showcases/wireless/fragmentation/, -f omnetpp.ini -c DCFfrag -r 0, 1s, 57ee-7ddf/tplx;dab9-5e8d/~tNl;7f9b-00fc/~tND;f985-34fb/tyf, PASS, wireless Ipv4 /showcases/wireless/fragmentation/, -f omnetpp.ini -c HCFfrag -r 0, 1s, f7a5-cf0a/tplx;a24a-a070/~tNl;7743-bba4/~tND;335d-6687/tyf, PASS, wireless Ipv4 -/showcases/wireless/fragmentation/, -f omnetpp.ini -c HCFfragblockack -r 0, 1s, 0702-c692/tplx;1bf9-b035/~tNl;9a04-4420/~tND;6f6e-b101/tyf, PASS, wireless Ipv4 +/showcases/wireless/fragmentation/, -f omnetpp.ini -c HCFfragblockack -r 0, 1s, 0702-c692/tplx;1bf9-b035/~tNl;f819-daf7/~tND;6f6e-b101/tyf, PASS, wireless Ipv4 /showcases/wireless/handover/, -f omnetpp.ini -c General -r 0, 250s, b287-6940/tplx;45c3-a292/~tNl;99ae-332d/~tND;02e9-9ad3/tyf, PASS, wireless @@ -314,7 +314,7 @@ /showcases/wireless/power/, -f omnetpp.ini -c General -r 0, 100s, 498f-b665/tplx;6f50-5caf/~tNl;0ad4-1089/~tND, PASS, wireless Ipv4 /showcases/wireless/qos/, -f omnetpp.ini -c NonQos -r 0, 10s, 37fd-5401/tplx;92f7-198c/~tNl;955c-ee96/~tND;093e-1ca4/tyf, PASS, wireless Ipv4 -/showcases/wireless/qos/, -f omnetpp.ini -c Qos -r 0, 10s, 1a49-72b3/tplx;e605-d79d/~tNl;10fc-7bb0/~tND;bda9-15d1/tyf, PASS, wireless Ipv4 +/showcases/wireless/qos/, -f omnetpp.ini -c Qos -r 0, 10s, 1a49-72b3/tplx;e605-d79d/~tNl;1f84-a860/~tND;bda9-15d1/tyf, PASS, wireless Ipv4 /showcases/wireless/ratecontrol/, -f omnetpp.ini -c NoRateControl -r 0, 14s, 7ee9-503a/tplx;0816-e58f/~tNl;648e-6e84/~tND;dad3-7f89/tyf, PASS, wireless Ipv4 /showcases/wireless/ratecontrol/, -f omnetpp.ini -c AarfRateControl -r 0, 12s, a7bc-05bb/tplx;9de0-4dd3/~tNl;1209-101b/~tND;7539-d32d/tyf, PASS, wireless Ipv4 diff --git a/tests/unit/Ieee80211BlockAckActionWire_1.test b/tests/unit/Ieee80211BlockAckActionWire_1.test new file mode 100644 index 00000000000..737be23e537 --- /dev/null +++ b/tests/unit/Ieee80211BlockAckActionWire_1.test @@ -0,0 +1,127 @@ +%description: +IEEE Std 802.11-2024 9.2.2, 9.4.1.13-16 and 9.6.4.2-4: independently encode and +parse golden ADDBA Request, ADDBA Response and DELBA octets, including common Action fields. + +%includes: +#include "inet/common/packet/Packet.h" +#include "inet/common/packet/chunk/BytesChunk.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" + +%global: +using namespace inet; +using namespace inet::ieee80211; + +static std::vector actionBytes(const std::vector& body) +{ + std::vector bytes = { + 0xD0, 0x00, 0x34, 0x12, + 0x02, 0, 0, 0, 0, 1, + 0x02, 0, 0, 0, 0, 2, + 0x02, 0, 0, 0, 0, 3, + 0x37, 0x12 + }; + bytes.insert(bytes.end(), body.begin(), body.end()); + return bytes; +} + +template +static Ptr makeAction() +{ + auto header = makeShared(); + header->setDurationField(SimTime(0x1234, SIMTIME_US)); + header->setReceiverAddress(MacAddress("02:00:00:00:00:01")); + header->setTransmitterAddress(MacAddress("02:00:00:00:00:02")); + header->setAddress3(MacAddress("02:00:00:00:00:03")); + header->setFragmentNumber(7); + header->setSequenceNumber(SequenceNumberCyclic(0x123)); + return header; +} + +template +static Ptr checkAction(const Ptr& header, const std::vector& body) +{ + auto expected = actionBytes(body); + Packet encoded("encoded", header); + ASSERT(header->getChunkLength() == B(expected.size())); + ASSERT(encoded.peekDataAsBytes()->getBytes() == expected); + + // Construct raw bytes independently; do not reuse the encoded packet or its serializer cache. + Packet raw("raw", makeShared(expected)); + auto parsed = raw.popAtFront(); + ASSERT(raw.getDataLength() == B(0)); + ASSERT(parsed->getChunkLength() == B(expected.size())); + ASSERT(parsed->getDurationField() == header->getDurationField()); + ASSERT(parsed->getReceiverAddress() == header->getReceiverAddress()); + ASSERT(parsed->getTransmitterAddress() == header->getTransmitterAddress()); + ASSERT(parsed->getAddress3() == header->getAddress3()); + ASSERT(parsed->getFragmentNumber() == 7); + ASSERT(parsed->getSequenceNumber() == SequenceNumberCyclic(0x123)); + ASSERT(parsed->getCategory() == 3); + ASSERT(parsed->getBlockAckAction() == header->getBlockAckAction()); + return parsed; +} + +%activity: +{ + auto header = makeAction(); + header->setDialogToken(9); + header->setAMsduSupported(true); + header->setBlockAckPolicy(true); + header->setTid(5); + header->setBufferSize(64); + header->setBlockAckTimeoutValue(SimTime(0x1234 * 1024, SIMTIME_US)); + header->setStartingSequenceNumber(SequenceNumberCyclic(0xABC)); + auto parsed = checkAction(header, {3, 0, 9, 0x17, 0x10, 0x34, 0x12, 0xC0, 0xAB}); + ASSERT(parsed->getDialogToken() == 9); + ASSERT(parsed->getAMsduSupported() && parsed->getBlockAckPolicy()); + ASSERT(parsed->getTid() == 5 && parsed->getBufferSize() == 64); + ASSERT(parsed->getBlockAckTimeoutValue() == header->getBlockAckTimeoutValue()); + ASSERT(parsed->get_fragmentNumber() == 0); + ASSERT(parsed->getStartingSequenceNumber() == SequenceNumberCyclic(0xABC)); +} +{ + auto header = makeAction(); + header->setDialogToken(9); + header->setStatusCode(37); + header->setAMsduSupported(false); + header->setBlockAckPolicy(true); + header->setTid(15); + header->setBufferSize(1023); + header->setBlockAckTimeoutValue(SimTime(65535 * 1024, SIMTIME_US)); + auto parsed = checkAction(header, {3, 1, 9, 0x25, 0, 0xFE, 0xFF, 0xFF, 0xFF}); + ASSERT(parsed->getDialogToken() == 9 && parsed->getStatusCode() == 37); + ASSERT(!parsed->getAMsduSupported() && parsed->getBlockAckPolicy()); + ASSERT(parsed->getTid() == 15 && parsed->getBufferSize() == 1023); + ASSERT(parsed->getBlockAckTimeoutValue() == header->getBlockAckTimeoutValue()); +} +{ + auto header = makeAction(); + header->setBlockAckPolicy(true); + header->setStartingSequenceNumber(SequenceNumberCyclic(0)); + auto parsed = checkAction(header, {3, 0, 1, 2, 0, 0, 0, 0, 0}); + ASSERT(!parsed->getAMsduSupported() && parsed->getBlockAckPolicy()); + ASSERT(parsed->getTid() == 0 && parsed->getBufferSize() == 0); + ASSERT(parsed->getBlockAckTimeoutValue() == SIMTIME_ZERO); + ASSERT(parsed->getStartingSequenceNumber() == SequenceNumberCyclic(0)); +} +{ + auto header = makeAction(); + header->setInitiator(true); + header->setTid(5); + header->setReasonCode(37); + auto parsed = checkAction(header, {3, 2, 0, 0x58, 0x25, 0}); + ASSERT(parsed->getReserved() == 0 && parsed->getInitiator()); + ASSERT(parsed->getTid() == 5 && parsed->getReasonCode() == 37); +} +{ + auto header = makeAction(); + header->setTid(15); + header->setReasonCode(39); + auto parsed = checkAction(header, {3, 2, 0, 0xF0, 0x27, 0}); + ASSERT(parsed->getReserved() == 0 && !parsed->getInitiator()); + ASSERT(parsed->getTid() == 15 && parsed->getReasonCode() == 39); +} +EV << "Block Ack Action fields match IEEE golden octets.\n"; + +%contains: stdout +Block Ack Action fields match IEEE golden octets. diff --git a/tests/unit/Ieee80211MgmtFrameSerializer_1.test b/tests/unit/Ieee80211MgmtFrameSerializer_1.test index f387d6b4c81..18e3638f495 100644 --- a/tests/unit/Ieee80211MgmtFrameSerializer_1.test +++ b/tests/unit/Ieee80211MgmtFrameSerializer_1.test @@ -455,6 +455,23 @@ ASSERT(unsupportedTypeRejected); wait(SimTime(0x12345678, SIMTIME_US) - simTime()); +// IEEE Std 802.11-2024, 9.4.1.10 and 11.1.3.1. TSF is an unsigned microsecond +// counter, not a simulation tick count. Since TSF is not retained, even values +// beyond SimTime's range must be consumed without disturbing subsequent fields. +for (const auto& timestampBytes : std::vector>{ + {0x40, 0x42, 0x0F, 0, 0, 0, 0, 0}, + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}) { + auto body = timestampBytes; + const std::vector fields = {0x34, 0x12, 0, 0, 0, 3, 'a', 'b', 'c', 1, 1, 0x0C}; + body.insert(body.end(), fields.begin(), fields.end()); + auto decodedBeacon = deserializeBody(body); + auto decodedProbe = deserializeBody(body); + ASSERT(decodedBeacon->getBeaconInterval() == SimTime(0x1234 * 1024, SIMTIME_US)); + ASSERT(decodedProbe->getBeaconInterval() == decodedBeacon->getBeaconInterval()); + ASSERT(std::string(decodedBeacon->getSSID()) == "abc"); + ASSERT(std::string(decodedProbe->getSSID()) == "abc"); +} + auto beacon = makeShared(); beacon->setSSID("abc"); beacon->setBeaconInterval(SimTime(0x1234 * 1024, SIMTIME_US)); diff --git a/tests/unit/Ieee80211MpduDelimiterWire_1.test b/tests/unit/Ieee80211MpduDelimiterWire_1.test new file mode 100644 index 00000000000..52672a65b36 --- /dev/null +++ b/tests/unit/Ieee80211MpduDelimiterWire_1.test @@ -0,0 +1,61 @@ +%description: +IEEE Std 802.11-2024 9.7.1/9.7.2: HT delimiter length, CRC and signature match +independent golden bytes. Corrupted delimiters are marked incorrect; oversized lengths are rejected. + +%includes: +#include "inet/common/packet/Packet.h" +#include "inet/common/packet/chunk/BytesChunk.h" +#include "inet/linklayer/ieee80211/mac/Ieee80211Frame_m.h" + +%global: +using namespace inet; +using namespace inet::ieee80211; + +%activity: +// Golden CRCs obtained by polynomial long division by x^8+x^2+x+1 over the +// B0-first input, with all-one initial remainder, then complement and wire bit ordering. +const int lengths[] = {0, 1, 255, 256, 0xABC, 4095}; +const std::vector> expected = { + {0x00, 0x00, 0x14, 0x4E}, + {0x10, 0x00, 0x01, 0x4E}, + {0xF0, 0x0F, 0xAC, 0x4E}, + {0x00, 0x10, 0x08, 0x4E}, + {0xC0, 0xAB, 0x4C, 0x4E}, + {0xF0, 0xFF, 0x18, 0x4E} +}; +for (size_t i = 0; i < expected.size(); i++) { + auto header = makeShared(); + header->setLength(lengths[i]); + Packet output("delimiter", header); + ASSERT(output.peekDataAsBytes()->getBytes() == expected[i]); + Packet input("golden", makeShared(expected[i])); + auto parsed = input.popAtFront(); + ASSERT(parsed->getLength() == lengths[i]); + ASSERT(parsed->getChunkLength() == B(4)); + ASSERT(!parsed->isIncorrect() && !parsed->isIncomplete()); + ASSERT(input.getDataLength() == B(0)); +} +for (int byteIndex : {0, 1, 2, 3}) { + auto corrupt = expected[4]; + corrupt[byteIndex] ^= 1; + Packet input("corrupt", makeShared(corrupt)); + auto parsed = input.popAtFront(B(4), Chunk::PF_ALLOW_INCORRECT); + ASSERT(parsed->isIncorrect()); +} +for (int length : {-1, 4096}) { + auto header = makeShared(); + header->setLength(length); + Packet output("invalid-length", header); + bool rejected = false; + try { + output.peekDataAsBytes(); + } + catch (const cRuntimeError&) { + rejected = true; + } + ASSERT(rejected); +} +EV << "HT delimiters match IEEE golden octets and reject corrupt representations.\n"; + +%contains: stdout +HT delimiters match IEEE golden octets and reject corrupt representations. diff --git a/tests/unit/Ieee80211MpduSubframeHeaderSerializer_1.test b/tests/unit/Ieee80211MpduSubframeHeaderSerializer_1.test index 601e4c45f3d..cabcf21723b 100644 --- a/tests/unit/Ieee80211MpduSubframeHeaderSerializer_1.test +++ b/tests/unit/Ieee80211MpduSubframeHeaderSerializer_1.test @@ -33,8 +33,8 @@ for (int length : {0, 255, 256, 0xABC, 4095}) { checkDecodedLength(bytes, length); } -// Fixed input in the existing encoder's format, independent of the round trip. -checkDecodedLength({0x0A, 0xBC, 0x00, 0x4E}, 0xABC); +// Fixed HT delimiter bytes, independent of the round trip. +checkDecodedLength({0xC0, 0xAB, 0x4C, 0x4E}, 0xABC); EV << "MPDU subframe lengths preserved across the 8-bit boundary.\n"; diff --git a/tests/unit/Ieee80211MsduAggregation_1.test b/tests/unit/Ieee80211MsduAggregation_1.test new file mode 100644 index 00000000000..c6b142c9e29 --- /dev/null +++ b/tests/unit/Ieee80211MsduAggregation_1.test @@ -0,0 +1,110 @@ +%description: +A-MSDU aggregation preserves TA and the resulting ACK target in all four DS directions. +Aggregation and deaggregation declare the actual 26/32-byte QoS header size and preserve +subframe payloads and source/destination addresses. IEEE Std 802.11-2024 9.3.2.1/9.3.2.2. +Outer Address3/Address4 bytes follow the Basic A-MSDU BSSID rules in Table 9-60. + +%includes: +#include "inet/common/packet/Packet.h" +#include "inet/common/packet/chunk/BytesChunk.h" +#include "inet/linklayer/ieee80211/mac/aggregation/MsduAggregation.h" +#include "inet/linklayer/ieee80211/mac/aggregation/MsduDeaggregation.h" +#include "inet/linklayer/ieee80211/mac/recipient/RecipientAckProcedure.h" +#include "inet/linklayer/ieee80211/mac/sequencenumberassignment/QoSSequenceNumberAssignment.h" + +%global: +using namespace inet; +using namespace inet::ieee80211; + +class TestAckProcedure : public RecipientAckProcedure +{ + public: + using RecipientAckProcedure::buildAck; +}; + +%activity: +const MacAddress ra("02:00:00:00:00:01"); +const MacAddress ta("02:00:00:00:00:02"); +const MacAddress address3("02:00:00:00:00:03"); +const MacAddress address4("02:00:00:00:00:04"); +MsduAggregation aggregation; +MsduDeaggregation deaggregation; +TestAckProcedure ackProcedure; +QoSSequenceNumberAssignment sequenceNumbers; +// Independently specified Address3 bytes for (ToDS, FromDS) = 00, 01, 10, 11. +// The four-address case models AP-to-AP transmission: BSSID is the transmitting AP. +const std::vector> expectedAddress3 = { + {0x02, 0, 0, 0, 0, 0x03}, + {0x02, 0, 0, 0, 0, 0x02}, + {0x02, 0, 0, 0, 0, 0x01}, + {0x02, 0, 0, 0, 0, 0x02} +}; +const std::vector expectedAddress4 = {0x02, 0, 0, 0, 0, 0x02}; +for (bool toDS : {false, true}) { + for (bool fromDS : {false, true}) { + std::vector frames; + const B headerLength(toDS && fromDS ? 32 : 26); + const std::vector> payloads = { + {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10, 11} + }; + for (size_t i = 0; i < payloads.size(); i++) { + auto header = makeShared(); + header->setType(ST_DATA_WITH_QOS); + header->setToDS(toDS); + header->setFromDS(fromDS); + header->setReceiverAddress(ra); + header->setTransmitterAddress(ta); + header->setAddress3(address3); + header->setAddress4(address4); + header->setTid(5); + header->setChunkLength(headerLength); + auto frame = new Packet(i == 0 ? "first" : "second", header); + frame->insertAtBack(makeShared(payloads[i])); + frame->insertAtBack(makeShared()); + frames.push_back(frame); + } + auto aggregate = aggregation.aggregateFrames(&frames); + // OriginatorQosMacDataService assigns the sequence after aggregation, before transmission. + auto sequencedHeader = aggregate->removeAtFront(); + sequenceNumbers.assignSequenceNumber(sequencedHeader); + aggregate->insertAtFront(sequencedHeader); + auto outer = aggregate->peekAtFront(); + ASSERT(outer->getReceiverAddress() == ra); + ASSERT(outer->getTransmitterAddress() == ta); + ASSERT(outer->getToDS() == toDS && outer->getFromDS() == fromDS); + ASSERT(outer->getTid() == 5 && outer->getAMsduPresent()); + ASSERT(outer->getChunkLength() == headerLength); + Packet outerPacket("outer", outer); + ASSERT(outerPacket.peekDataAsBytes()->getChunkLength() == headerLength); + const auto bytes = outerPacket.peekDataAsBytes()->getBytes(); + ASSERT(std::vector(bytes.begin() + 16, bytes.begin() + 22) == expectedAddress3[2 * toDS + fromDS]); + if (toDS && fromDS) + ASSERT(std::vector(bytes.begin() + 24, bytes.begin() + 30) == expectedAddress4); + ASSERT(ackProcedure.buildAck(outer)->getReceiverAddress() == ta); + + auto restored = deaggregation.deaggregateFrame(aggregate); + ASSERT(restored->size() == payloads.size()); + for (size_t i = 0; i < restored->size(); i++) { + auto frame = restored->at(i); + auto header = frame->popAtFront(); + ASSERT(header->getTransmitterAddress() == ta); + ASSERT(header->getReceiverAddress() == ra); + ASSERT(header->getChunkLength() == headerLength); + ASSERT(header->getToDS() == toDS && header->getFromDS() == fromDS); + ASSERT(header->getTid() == 5 && !header->getAMsduPresent()); + ASSERT(header->getAddress3() == address3); + if (toDS && fromDS) + ASSERT(header->getAddress4() == address4); + Packet headerPacket("restored-header", header); + ASSERT(headerPacket.peekDataAsBytes()->getChunkLength() == headerLength); + frame->popAtBack(B(4)); + ASSERT(frame->peekDataAsBytes()->getBytes() == payloads[i]); + delete frame; + } + delete restored; + } +} +EV << "A-MSDU TA, ACK target, header lengths and payloads are preserved.\n"; + +%contains: stdout +A-MSDU TA, ACK target, header lengths and payloads are preserved.