/* * 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 "remote_connection.h" #include "hwsim.h" #include "frame.h" #include "log.h" #include #include #include #include #include #include static const char kQemuPipeName[] = "qemud:wififorward"; static const size_t kReceiveBufferIncrement = 32768; static const size_t kReceiveBufferMaxSize = 1 << 20; static const uint8_t kWifiForwardVersion = 0x01; static const uint32_t kWifiForwardMagic = 0xD6C4B3A2; // This matches with the kernel constant IEEE80211_TX_MAX_RATES in // include/net/mac80211.h in the kernel tree. static const uint32_t kMaxNumRates = 4; struct WifiForwardHeader { WifiForwardHeader(FrameType type, const MacAddress& transmitter, uint32_t fullLength, uint64_t cookie, uint32_t flags, uint32_t channel, uint32_t numRates, const hwsim_tx_rate* txRates) : magic(__cpu_to_le32(kWifiForwardMagic)) , version(kWifiForwardVersion) , type(static_cast(type)) , transmitter(transmitter) , dataOffset(sizeof(WifiForwardHeader)) , fullLength(__cpu_to_le32(fullLength)) , cookie(__cpu_to_le64(cookie)) , flags(__cpu_to_le32(flags)) , channel(__cpu_to_le32(channel)) , numRates(__cpu_to_le32(numRates)) { memcpy(rates, txRates, std::min(numRates, kMaxNumRates)); if (numRates < kMaxNumRates) { memset(&rates[numRates], 0, sizeof(rates[0]) * (kMaxNumRates - numRates)); } } uint32_t magic; uint8_t version; uint8_t type; MacAddress transmitter; uint16_t dataOffset; uint32_t fullLength; uint64_t cookie; uint32_t flags; uint32_t channel; uint32_t numRates; hwsim_tx_rate rates[kMaxNumRates]; } __attribute__((__packed__)); RemoteConnection::RemoteConnection(OnFrameCallback onFrameCallback, OnAckCallback onAckCallback, OnErrorCallback onErrorCallback) : mOnFrameCallback(onFrameCallback) , mOnAckCallback(onAckCallback) , mOnErrorCallback(onErrorCallback) { } RemoteConnection::~RemoteConnection() { if (mPipeFd != -1) { ::close(mPipeFd); mPipeFd = -1; } } Result RemoteConnection::init() { if (mPipeFd != -1) { return Result::error("RemoteConnetion already initialized"); } mPipeFd = qemu_pipe_open_ns(NULL, kQemuPipeName, O_RDWR); if (mPipeFd == -1) { return Result::error("RemoteConnection failed to open pipe"); } return Result::success(); } Pollable::Timestamp RemoteConnection::getTimeout() const { // If there is no pipe return the deadline, we're going to retry, otherwise // use an infinite timeout. return mPipeFd == -1 ? mDeadline : Pollable::Timestamp::max(); } void RemoteConnection::receive() { size_t start = mBuffer.size(); size_t newSize = start + kReceiveBufferIncrement; if (newSize > kReceiveBufferMaxSize) { // We've exceeded the maximum allowed size, drop everything we have so // far and start over. This is most likely caused by some delay in // injection or the injection failing in which case keeping old data // around isn't going to be very useful. ALOGE("RemoteConnection ran out of buffer space"); newSize = kReceiveBufferIncrement; start = 0; } mBuffer.resize(newSize); while (true) { int result = ::read(mPipeFd, mBuffer.data() + start, mBuffer.size() - start); if (result < 0) { if (errno == EINTR) { continue; } ALOGE("RemoteConnection failed to read to forward buffer: %s", strerror(errno)); // Return the buffer to its previous size mBuffer.resize(start); return; } else if (result == 0) { // Nothing received, nothing to write // Return the buffer to its previous size mBuffer.resize(start); ALOGE("RemoteConnection did not receive anything to inject"); return; } // Adjust the buffer size to match everything we recieved mBuffer.resize(start + static_cast(result)); break; } while (mBuffer.size() >= sizeof(WifiForwardHeader)) { auto fwd = reinterpret_cast(mBuffer.data()); if (__le32_to_cpu(fwd->magic) != kWifiForwardMagic) { // We are not properly aligned, this can happen for the first read // if the client or server happens to send something that's in the // middle of a stream. Attempt to find the next packet boundary. ALOGE("RemoteConnection found incorrect magic, finding next magic"); uint32_t le32magic = __cpu_to_le32(kWifiForwardMagic); auto next = reinterpret_cast( ::memmem(mBuffer.data(), mBuffer.size(), &le32magic, sizeof(le32magic))); if (next) { // We've found a possible candidate, erase everything before size_t length = next - mBuffer.data(); mBuffer.erase(mBuffer.begin(), mBuffer.begin() + length); continue; } else { // There is no possible candidate, drop everything except the // last three bytes. The last three bytes could possibly be the // start of the next magic without actually triggering the // search above. if (mBuffer.size() > 3) { mBuffer.erase(mBuffer.begin(), mBuffer.end() - 3); } // In this case there is nothing left to parse so just return // right away. return; } } // Check if we support this version if (fwd->version != kWifiForwardVersion) { // Unsupported version if (!mReportedVersionMismatches.test(fwd->version)) { // Only report these once per version or this might become // very spammy. ALOGE("RemoteConnection encountered unknown version %u", fwd->version); mReportedVersionMismatches.set(fwd->version); } // Drop the magic from the buffer and attempt to find the next magic mBuffer.erase(mBuffer.begin(), mBuffer.begin() + 4); continue; } // The length according to the wifi forward header const uint32_t fullLength = __le32_to_cpu(fwd->fullLength); const uint16_t offset = __le16_to_cpu(fwd->dataOffset); if (offset < sizeof(WifiForwardHeader) || offset > fullLength) { // The frame offset is not large enough to go past the header // or it's outside of the bounds of the length of the frame. ALOGE("Invalid data offset in header %u, full length is %u", offset, fullLength); // Erase the magic and try again mBuffer.erase(mBuffer.begin(), mBuffer.begin() + 4); continue; } const size_t frameLength = fullLength - offset; if (fullLength > mBuffer.size()) { // We have not received enough data yet, wait for more to arrive. return; } FrameType type = frameTypeFromByte(fwd->type); if (frameLength == 0 && type != FrameType::Ack) { ALOGE("Received empty frame for non-ack frame"); return; } unsigned char* frameData = mBuffer.data() + offset; if (type == FrameType::Ack) { FrameInfo info(fwd->transmitter, __le64_to_cpu(fwd->cookie), __le32_to_cpu(fwd->flags), __le32_to_cpu(fwd->channel), fwd->rates, __le32_to_cpu(fwd->numRates)); if (info.flags() & HWSIM_TX_STAT_ACK) { mOnAckCallback(info); } else { mOnErrorCallback(info); } } else if (type == FrameType::Data) { auto frame = std::make_unique(frameData, frameLength, fwd->transmitter, __le64_to_cpu(fwd->cookie), __le32_to_cpu(fwd->flags), __le32_to_cpu(fwd->channel), fwd->rates, __le32_to_cpu(fwd->numRates)); mOnFrameCallback(std::move(frame)); } else { ALOGE("Received unknown message type %u from remote", static_cast(fwd->type)); } mBuffer.erase(mBuffer.begin(), mBuffer.begin() + fullLength); } } bool RemoteConnection::sendFrame(std::unique_ptr frame) { if (mPipeFd == -1) { ALOGE("RemoteConnection unable to forward data, pipe not open"); return false; } WifiForwardHeader header(FrameType::Data, frame->transmitter(), frame->size() + sizeof(WifiForwardHeader), frame->cookie(), frame->flags(), frame->channel(), frame->rates().size(), frame->rates().data()); #if 1 constexpr size_t count = 2; struct iovec iov[count]; iov[0].iov_base = &header; iov[0].iov_len = sizeof(header); iov[1].iov_base = frame->data(); iov[1].iov_len = frame->size(); size_t totalSize = iov[0].iov_len + iov[1].iov_len; size_t current = 0; for (;;) { ssize_t written = ::writev(mPipeFd, iov + current, count - current); if (written < 0) { ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno)); return false; } if (static_cast(written) == totalSize) { // Optimize for most common case, everything was written break; } totalSize -= written; // Determine how much is left to write after this while (current < count && written >= iov[current].iov_len) { written -= iov[current++].iov_len; } if (current == count) { break; } iov[current].iov_base = reinterpret_cast(iov[current].iov_base) + written; iov[current].iov_len -= written; } #else if (qemu_pipe_write_fully(mPipeFd, &header, sizeof(header))) { ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno)); return false; } if (qemu_pipe_write_fully(mPipeFd, frame->data(), frame->size())) { ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno)); return false; } #endif return true; } bool RemoteConnection::ackFrame(FrameInfo& info, bool success) { uint32_t flags = info.flags(); if (success) { flags |= HWSIM_TX_STAT_ACK; } WifiForwardHeader header(FrameType::Ack, info.transmitter(), sizeof(WifiForwardHeader), info.cookie(), flags, info.channel(), info.rates().size(), info.rates().data()); if (qemu_pipe_write_fully(mPipeFd, &header, sizeof(header))) { ALOGE("RemoteConnection failed to write to pipe: %s", strerror(errno)); return false; } return true; }