blob: 1cebc17e63e986dfd58345cc1e848dc94423f803 [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 "local_connection.h"
#include "hwsim.h"
#include "log.h"
#include "macaddress.h"
#include "netlink_message.h"
#include <net/ethernet.h>
#include <netlink/netlink.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/genl.h>
#include <utils/CallStack.h>
#include <inttypes.h>
#include <stdlib.h>
static const char kHwSimFamilyName[] = "MAC80211_HWSIM";
static const int kHwSimVersion = 1;
static const unsigned int kDefaultSignalStrength = -50;
static const int kDefaultSocketBufferSize = 8 * (1 << 20);
static uint32_t getSeqNum(struct nl_msg* msg) {
return nlmsg_hdr(msg)->nlmsg_seq;
}
static int onSent(struct nl_msg* , void*) {
return NL_OK;
}
static int onSeqCheck(struct nl_msg* msg, void*) {
uint32_t seq = getSeqNum(msg);
return seq == 0 ? NL_SKIP : NL_OK;
}
LocalConnection::LocalConnection(OnFrameCallback onFrameCallback,
OnAckCallback onAckCallback,
OnErrorCallback onErrorCallback)
: mOnFrameCallback(onFrameCallback)
, mOnAckCallback(onAckCallback)
, mOnErrorCallback(onErrorCallback) {
}
Result LocalConnection::init(std::chrono::steady_clock::time_point now) {
Result res = mNetlinkSocket.init();
if (!res) { return res; }
res = mNetlinkSocket.setOnMsgInCallback(staticOnMessage, this);
if (!res) { return res; }
res = mNetlinkSocket.setOnMsgOutCallback(onSent, this);
if (!res) { return res; }
res = mNetlinkSocket.setOnSeqCheckCallback(onSeqCheck, this);
if (!res) { return res; }
res = mNetlinkSocket.setOnAckCallback(staticOnAck, this);
if (!res) { return res; }
res = mNetlinkSocket.setOnErrorCallback(staticOnError, this);
if (!res) { return res; }
res = mNetlinkSocket.connectGeneric();
if (!res) { return res; }
res = mNetlinkSocket.setBufferSizes(kDefaultSocketBufferSize,
kDefaultSocketBufferSize);
if (!res) { return res; }
mNetlinkFamily = mNetlinkSocket.resolveNetlinkFamily(kHwSimFamilyName);
if (mNetlinkFamily < 0) {
return Result::error("Failed to resolve netlink family name: %s",
nl_geterror(mNetlinkFamily));
}
mPendingFrames.setCurrentTime(now);
mSequenceNumberCookies.setCurrentTime(now);
mLastCacheTimeUpdate = now;
mLastCacheExpiration = now;
return registerReceiver();
}
int LocalConnection::getFd() const {
return mNetlinkSocket.getFd();
}
bool LocalConnection::receive() {
return mNetlinkSocket.receive();
}
uint32_t LocalConnection::transferFrame(std::unique_ptr<Frame> frame,
const MacAddress& dest) {
NetlinkMessage msg;
if (!msg.initGeneric(mNetlinkFamily, HWSIM_CMD_FRAME, kHwSimVersion)) {
ALOGE("LocalConnection transferFrame failed to init msg");
return 0;
}
frame->incrementAttempts();
if (!msg.addAttribute(HWSIM_ATTR_ADDR_RECEIVER, dest.addr, ETH_ALEN) ||
!msg.addAttribute(HWSIM_ATTR_FRAME, frame->data(), frame->size()) ||
!msg.addAttribute(HWSIM_ATTR_RX_RATE, 1u) ||
!msg.addAttribute(HWSIM_ATTR_SIGNAL, kDefaultSignalStrength) ||
!msg.addAttribute(HWSIM_ATTR_FREQ, frame->channel())) {
ALOGE("LocalConnection transferFrame failed to set attrs");
return 0;
}
if (!mNetlinkSocket.send(msg)) {
return 0;
}
// Store the radio destination for potential retransmissions.
frame->setRadioDestination(dest);
uint32_t seqNum = msg.getSeqNum();
uint64_t cookie = frame->cookie();
FrameId id(cookie, frame->transmitter());
mSequenceNumberCookies[seqNum] = id;
mPendingFrames[id] = std::move(frame);
return seqNum;
}
uint32_t LocalConnection::cloneFrame(const Frame& frame,
const MacAddress& dest) {
auto copy = std::make_unique<Frame>(frame.data(),
frame.size(),
frame.transmitter(),
frame.cookie(),
frame.flags(),
frame.channel(),
frame.rates().data(),
frame.rates().size());
return transferFrame(std::move(copy), dest);
}
bool LocalConnection::ackFrame(FrameInfo& info, bool success) {
NetlinkMessage msg;
if (!msg.initGeneric(mNetlinkFamily,
HWSIM_CMD_TX_INFO_FRAME,
kHwSimVersion)) {
ALOGE("LocalConnection ackFrame failed to create msg");
return false;
}
uint32_t flags = info.flags();
if (success) {
flags |= HWSIM_TX_STAT_ACK;
}
const uint8_t* transmitter = info.transmitter().addr;
const Rates& rates = info.rates();
if (!msg.addAttribute(HWSIM_ATTR_ADDR_TRANSMITTER, transmitter, ETH_ALEN) ||
!msg.addAttribute(HWSIM_ATTR_TX_INFO, rates.data(), rates.size()) ||
!msg.addAttribute(HWSIM_ATTR_FLAGS, flags) ||
!msg.addAttribute(HWSIM_ATTR_SIGNAL, kDefaultSignalStrength) ||
!msg.addAttribute(HWSIM_ATTR_COOKIE, info.cookie())) {
ALOGE("LocalConnection ackFrame failed to set attributes");
return false;
}
if (!mNetlinkSocket.send(msg)) {
return false;
}
mPendingFrames.erase(FrameId(info.cookie(), info.transmitter()));
return true;
}
std::chrono::steady_clock::time_point LocalConnection::getTimeout() const {
if (mRetryQueue.empty()) {
return std::chrono::steady_clock::time_point::max();
}
return mRetryQueue.top().first;
}
void LocalConnection::onTimeout(std::chrono::steady_clock::time_point now) {
if (now - mLastCacheTimeUpdate > std::chrono::seconds(1)) {
// Only update the time once per second, there's no need for a super
// high resolution here. We just want to make sure these caches don't
// fill up over a long period of time.
mPendingFrames.setCurrentTime(now);
mSequenceNumberCookies.setCurrentTime(now);
mLastCacheTimeUpdate = now;
}
if (now - mLastCacheExpiration > std::chrono::seconds(10)) {
// Only expire entries every 10 seconds, this is an operation that has
// some cost to it and doesn't have to happen very often.
mPendingFrames.expireEntries();
mSequenceNumberCookies.expireEntries();
mLastCacheExpiration = now;
}
while (!mRetryQueue.empty() && now >= mRetryQueue.top().first) {
FrameId id = mRetryQueue.top().second;
auto frameIt = mPendingFrames.find(id);
if (frameIt != mPendingFrames.end()) {
// Frame is still available, retry it
std::unique_ptr<Frame> frame = std::move(frameIt->second);
mPendingFrames.erase(frameIt);
MacAddress dest = frame->radioDestination();
transferFrame(std::move(frame), dest);
}
mRetryQueue.pop();
}
}
Result LocalConnection::registerReceiver() {
NetlinkMessage msg;
if (!msg.initGeneric(mNetlinkFamily, HWSIM_CMD_REGISTER, kHwSimVersion)) {
return Result::error("Failed to create register receiver message");
}
if (!mNetlinkSocket.send(msg)) {
return Result::error("Failed to send register receiver message");
}
return Result::success();
}
int LocalConnection::staticOnMessage(struct nl_msg* msg, void* context) {
if (!context) {
return NL_SKIP;
}
auto connection = static_cast<LocalConnection*>(context);
return connection->onMessage(msg);
}
int LocalConnection::staticOnAck(struct nl_msg* msg, void* context) {
if (!context) {
return NL_SKIP;
}
auto connection = static_cast<LocalConnection*>(context);
return connection->onAck(msg);
}
int LocalConnection::staticOnError(struct sockaddr_nl* addr,
struct nlmsgerr* error,
void* context) {
if (!context) {
return NL_SKIP;
}
auto connection = static_cast<LocalConnection*>(context);
return connection->onError(addr, error);
}
int LocalConnection::onMessage(struct nl_msg* msg) {
struct nlmsghdr* hdr = nlmsg_hdr(msg);
auto generic = reinterpret_cast<const struct genlmsghdr*>(nlmsg_data(hdr));
switch (generic->cmd) {
case HWSIM_CMD_FRAME:
return onFrame(msg);
}
return NL_OK;
}
int LocalConnection::onFrame(struct nl_msg* msg) {
struct nlmsghdr* hdr = nlmsg_hdr(msg);
std::unique_ptr<Frame> frame = parseFrame(hdr);
if (!frame) {
return NL_SKIP;
}
mOnFrameCallback(std::move(frame));
return NL_OK;
}
std::unique_ptr<Frame> LocalConnection::parseFrame(struct nlmsghdr* hdr) {
struct nlattr* attrs[HWSIM_ATTR_MAX + 1];
genlmsg_parse(hdr, 0, attrs, HWSIM_ATTR_MAX, nullptr);
if (!attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) {
ALOGE("Received cmd frame without transmitter address");
return nullptr;
}
if (!attrs[HWSIM_ATTR_TX_INFO]) {
ALOGE("Received cmd frame without tx rates");
return nullptr;
}
if (!attrs[HWSIM_ATTR_FREQ]) {
ALOGE("Recieved cmd frame without channel frequency");
return nullptr;
}
uint64_t cookie = nla_get_u64(attrs[HWSIM_ATTR_COOKIE]);
const auto& source = *reinterpret_cast<const MacAddress*>(
nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]));
auto rates = reinterpret_cast<const hwsim_tx_rate*>(
nla_data(attrs[HWSIM_ATTR_TX_INFO]));
int rateLength = nla_len(attrs[HWSIM_ATTR_TX_INFO]);
// Make sure the length is valid, must be multiple of hwsim_tx_rate
if (rateLength <= 0 || (rateLength % sizeof(hwsim_tx_rate)) != 0) {
ALOGE("Invalid tx rate length %d", rateLength);
}
size_t numRates = static_cast<size_t>(rateLength) / sizeof(hwsim_tx_rate);
int length = nla_len(attrs[HWSIM_ATTR_FRAME]);
auto data = reinterpret_cast<uint8_t*>(nla_data(attrs[HWSIM_ATTR_FRAME]));
uint32_t flags = nla_get_u32(attrs[HWSIM_ATTR_FLAGS]);
uint32_t channel = nla_get_u32(attrs[HWSIM_ATTR_FREQ]);
return std::make_unique<Frame>(data, length, source, cookie,
flags, channel, rates, numRates);
}
int LocalConnection::onAck(struct nl_msg* msg) {
struct nlmsghdr* hdr = nlmsg_hdr(msg);
uint32_t seqNum = hdr->nlmsg_seq;
auto cookie = mSequenceNumberCookies.find(seqNum);
if (cookie == mSequenceNumberCookies.end()) {
// This is not a frame we sent. This is fairly common for libnl's
// internal use so don't log this.
return NL_SKIP;
}
auto pendingFrame = mPendingFrames.find(cookie->second);
// We don't need to keep this around anymore, erase it.
mSequenceNumberCookies.erase(seqNum);
if (pendingFrame == mPendingFrames.end()) {
// We have no cookie associated with this sequence number. This might
// happen if the remote connection already acked the frame and removed
// the frame info. Consider this resolved.
return NL_SKIP;
}
Frame* frame = pendingFrame->second.get();
mOnAckCallback(frame->info());
// Make sure to erase using the cookie instead of the iterator. The callback
// might have already erased this entry so the iterator could be invalid at
// this point. Instead of a fancy scheme of checking this just play it safe
// to allow the callback more freedom.
mPendingFrames.erase(cookie->second);
return NL_OK;
}
int LocalConnection::onError(struct sockaddr_nl*, struct nlmsgerr* error) {
struct nlmsghdr* hdr = &error->msg;
uint32_t seqNum = hdr->nlmsg_seq;
auto idIt = mSequenceNumberCookies.find(seqNum);
if (idIt == mSequenceNumberCookies.end()) {
return NL_SKIP;
}
FrameId id = idIt->second;
// No need to keep the sequence number around anymore, it's expired and is
// no longer useful.
mSequenceNumberCookies.erase(idIt);
auto frameIt = mPendingFrames.find(id);
if (frameIt == mPendingFrames.end()) {
return NL_SKIP;
}
Frame* frame = frameIt->second.get();
if (!frame->hasRemainingAttempts()) {
// This frame has used up all its attempts, there's nothing we can do
mOnErrorCallback(frame->info());
mPendingFrames.erase(id);
return NL_SKIP;
}
// Store the frame in the retry queue
uint64_t timeout = frame->calcNextTimeout();
auto deadline = std::chrono::steady_clock::now() +
std::chrono::microseconds(timeout);
mRetryQueue.emplace(deadline, id);
return NL_SKIP;
}