| /* |
| * 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 <errno.h> |
| #include <linux/kernel.h> |
| #include <netinet/in.h> |
| #include <qemu_pipe_bp.h> |
| #include <sys/uio.h> |
| #include <unistd.h> |
| |
| 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<uint16_t>(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<size_t>(result)); |
| break; |
| } |
| |
| while (mBuffer.size() >= sizeof(WifiForwardHeader)) { |
| auto fwd = reinterpret_cast<WifiForwardHeader*>(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<unsigned char*>( |
| ::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<Frame>(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<uint8_t>(fwd->type)); |
| } |
| |
| mBuffer.erase(mBuffer.begin(), mBuffer.begin() + fullLength); |
| } |
| } |
| |
| bool RemoteConnection::sendFrame(std::unique_ptr<Frame> 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<size_t>(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 && static_cast<size_t>(written) >= iov[current].iov_len) { |
| written -= iov[current++].iov_len; |
| } |
| if (current == count) { |
| break; |
| } |
| iov[current].iov_base = |
| reinterpret_cast<char*>(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; |
| } |