/* * 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 #include #include #include #include #include #include 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, 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.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 = 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(context); return connection->onMessage(msg); } int LocalConnection::staticOnAck(struct nl_msg* msg, void* context) { if (!context) { return NL_SKIP; } auto connection = static_cast(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(context); return connection->onError(addr, error); } int LocalConnection::onMessage(struct nl_msg* msg) { struct nlmsghdr* hdr = nlmsg_hdr(msg); auto generic = reinterpret_cast(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 = parseFrame(hdr); if (!frame) { return NL_SKIP; } mOnFrameCallback(std::move(frame)); return NL_OK; } std::unique_ptr 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( nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER])); auto rates = reinterpret_cast( 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(rateLength) / sizeof(hwsim_tx_rate); int length = nla_len(attrs[HWSIM_ATTR_FRAME]); auto data = reinterpret_cast(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(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; }