371 lines
10 KiB
C++
371 lines
10 KiB
C++
/*
|
|
* 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];
|
|
}
|