/* * Copyright 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "frame.h" #include "hwsim.h" #include "ieee80211.h" #include #include static constexpr uint64_t kSlotTime = 9; static const AccessCategory kPriorityToAc[8] = { AccessCategory::BestEffort, AccessCategory::Background, AccessCategory::Background, AccessCategory::BestEffort, AccessCategory::Video, AccessCategory::Video, AccessCategory::Voice, AccessCategory::Voice, }; static uint32_t calcContentionWindowMin(AccessCategory ac) { switch (ac) { case AccessCategory::Voice: return 3; case AccessCategory::Video: return 7; case AccessCategory::BestEffort: return 15; case AccessCategory::Background: return 15; } } static uint32_t calcContentionWindowMax(AccessCategory ac) { switch (ac) { case AccessCategory::Voice: return 7; case AccessCategory::Video: return 15; case AccessCategory::BestEffort: return 1023; case AccessCategory::Background: return 1023; } } FrameType frameTypeFromByte(uint8_t byte) { if (byte == static_cast(FrameType::Ack)) { return FrameType::Ack; } else if (byte == static_cast(FrameType::Data)) { return FrameType::Data; } return FrameType::Unknown; } FrameInfo::FrameInfo(const MacAddress& transmitter, uint64_t cookie, uint32_t flags, uint32_t channel, const hwsim_tx_rate* rates, size_t numRates) : mTransmitter(transmitter) , mCookie(cookie) , mFlags(flags) , mChannel(channel) { size_t i = 0; for (; i < numRates; ++i) { mTxRates[i].count = 0; mTxRates[i].idx = rates[i].idx; } for (; i < mTxRates.size(); ++i) { mTxRates[i].count = 0; mTxRates[i].idx = -1; } } bool FrameInfo::shouldAck() const { return !!(mFlags & HWSIM_TX_CTL_REQ_TX_STATUS); } Frame::Frame(const uint8_t* data, size_t size) : mData(data, data + size) { } Frame::Frame(const uint8_t* data, size_t size, const MacAddress& transmitter, uint64_t cookie, uint32_t flags, uint32_t channel, const hwsim_tx_rate* rates, size_t numRates) : mData(data, data + size) , mInfo(transmitter, cookie, flags, channel, rates, numRates) , mContentionWindow(calcContentionWindowMin(getAc())) , mContentionWindowMax(calcContentionWindowMax(getAc())) { memcpy(mInitialTxRates.data(), rates, numRates * sizeof(*rates)); } bool Frame::incrementAttempts() { Rates& rates = mInfo.rates(); for (size_t i = 0; i < rates.size(); ++i) { if (mInitialTxRates[i].idx == -1) { // We've run out of attempts break; } if (rates[i].count < mInitialTxRates[i].count) { ++rates[i].count; return true; } } return false; } bool Frame::hasRemainingAttempts() { Rates& rates = mInfo.rates(); for (size_t i = 0; i < rates.size(); ++i) { if (mInitialTxRates[i].idx == -1) { break; } if (rates[i].count < mInitialTxRates[i].count) { return true; } } return false; } const MacAddress& Frame::source() const { auto hdr = reinterpret_cast(mData.data()); return *reinterpret_cast(hdr->addr2); } const MacAddress& Frame::destination() const { auto hdr = reinterpret_cast(mData.data()); return *reinterpret_cast(hdr->addr1); } std::string Frame::str() const { uint8_t type = (mData[0] >> 2) & 0x3; uint8_t subType = (mData[0] >> 4) & 0x0F; std::stringstream ss; ss << "[ Ck: " << cookie() << " Ch: " << channel() << " ] "; switch (type) { case 0: // Management ss << "Management ("; switch (subType) { case 0: ss << "Association Request"; break; case 1: ss << "Association Response"; break; case 2: ss << "Reassociation Request"; break; case 3: ss << "Reassociation Response"; break; case 4: ss << "Probe Request"; break; case 5: ss << "Probe Response"; break; case 6: ss << "Timing Advertisement"; break; case 8: ss << "Beacon"; break; case 9: ss << "ATIM"; break; case 10: ss << "Disassociation"; break; case 11: ss << "Authentication"; break; case 12: ss << "Deauthentication"; break; case 13: ss << "Action"; break; case 14: ss << "Action No Ack"; break; default: ss << subType; break; } ss << ')'; break; case 1: // Control ss << "Control ("; switch (subType) { case 4: ss << "Beamforming Report Poll"; break; case 5: ss << "VHT NDP Announcement"; break; case 6: ss << "Control Frame Extension"; break; case 7: ss << "Control Wrapper"; break; case 8: ss << "Block Ack Request"; break; case 9: ss << "Block Ack"; break; case 10: ss << "PS-Poll"; break; case 11: ss << "RTS"; break; case 12: ss << "CTS"; break; case 13: ss << "Ack"; break; case 14: ss << "CF-End"; break; case 15: ss << "CF-End+CF-Ack"; break; default: ss << subType; break; } ss << ')'; break; case 2: // Data ss << "Data ("; switch (subType) { case 0: ss << "Data"; break; case 1: ss << "Data+CF-Ack"; break; case 2: ss << "Data+CF-Poll"; break; case 3: ss << "Data+CF-Ack+CF-Poll"; break; case 4: ss << "Null"; break; case 5: ss << "CF-Ack"; break; case 6: ss << "CF-Poll"; break; case 7: ss << "CF-Ack+CF-Poll"; break; case 8: ss << "QoS Data"; break; case 9: ss << "QoS Data+CF-Ack"; break; case 10: ss << "QoS Data+CF-Poll"; break; case 11: ss << "QoS Data+CF-Ack+CF-Poll"; break; case 12: ss << "QoS Null"; break; case 14: ss << "QoS CF-Poll"; break; case 15: ss << "QoS CF-Poll+CF-Ack"; break; default: ss << subType; break; } ss << ')'; break; case 3: // Extension ss << "Extension ("; switch (subType) { case 0: ss << "DMG Beacon"; break; default: ss << subType; break; } ss << ')'; break; default: ss << type << " (" << subType << ')'; break; } return ss.str(); } bool Frame::isBeacon() const { uint8_t totalType = mData[0] & 0xFC; return totalType == 0x80; } bool Frame::isData() const { uint8_t totalType = mData[0] & 0x0C; return totalType == 0x08; } bool Frame::isDataQoS() const { uint8_t totalType = mData[0] & 0xFC; return totalType == 0x88; } uint16_t Frame::getQoSControl() const { // Some frames have 4 address fields instead of 3 which pushes QoS control // forward 6 bytes. bool uses4Addresses = !!(mData[1] & 0x03); const uint8_t* addr = &mData[uses4Addresses ? 30 : 24]; return __le16_to_cpu(*reinterpret_cast(addr)); } uint64_t Frame::calcNextTimeout() { mNextTimeout = (mContentionWindow * kSlotTime) / 2; mContentionWindow = std::min((mContentionWindow * 2) + 1, mContentionWindowMax); return mNextTimeout; } AccessCategory Frame::getAc() const { if (!isData()) { return AccessCategory::Voice; } if (!isDataQoS()) { return AccessCategory::BestEffort; } uint16_t priority = getQoSControl() & 0x07; return kPriorityToAc[priority]; }