blob: 3b20b4e14714cbde18b6449eb2440a5c771346ae [file] [log] [blame]
/*
* 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 <asm/byteorder.h>
#include <sstream>
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<uint8_t>(FrameType::Ack)) {
return FrameType::Ack;
} else if (byte == static_cast<uint8_t>(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<const struct ieee80211_hdr*>(mData.data());
return *reinterpret_cast<const MacAddress*>(hdr->addr2);
}
const MacAddress& Frame::destination() const {
auto hdr = reinterpret_cast<const struct ieee80211_hdr*>(mData.data());
return *reinterpret_cast<const MacAddress*>(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<const uint16_t*>(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];
}