blob: b56c7cdbb9b922523ac19dd72dccdf4daecb9648 [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 "wifi_forwarder.h"
#include "frame.h"
#include "log.h"
static constexpr bool kDebugTraffic = false;
static constexpr bool kDebugBeaconTraffic = false;
// How many seconds to keep aliases alive for. Since this is used to keep track
// of randomized MAC addresses they will expire after a while. Set the value
// pretty high to ensure that we don't accidentally lose entries just because
// there's not a lot of frames going through.
static constexpr auto kAliasesCacheTimeout = std::chrono::hours(8);
WifiForwarder::WifiForwarder()
: mAliases(kAliasesCacheTimeout)
, mLocalConnection([this](std::unique_ptr<Frame> frame) {
forwardFrame(std::move(frame), RadioType::Local);
},
[this](FrameInfo& info) { onAck(info, true); },
[this](FrameInfo& info) { onAck(info, false); })
, mRemoteConnection([this](std::unique_ptr<Frame> frame) {
forwardFrame(std::move(frame), RadioType::Remote);
},
[this](FrameInfo& info) { onAck(info, true); },
[this](FrameInfo& info) { onAck(info, false); }) {
}
Result WifiForwarder::init() {
auto now = Pollable::Clock::now();
Result res = mRemoteConnection.init();
if (!res) {
// It's OK if this fails, the emulator might not have been started with
// this feature enabled. If it's not enabled we'll try again later. If
// this does not succeed we don't need to perform the rest of the
// initialization either. Just let WiFi work the same as normal.
ALOGE("RemoteConnection failed to initialize: %s", res.c_str());
mInitDeadline = now + std::chrono::minutes(1);
return Result::success();
}
mAliases.setCurrentTime(now);
res = mLocalConnection.init(now);
if (!res) {
return res;
}
mDeadline = now + std::chrono::seconds(1);
return Result::success();
}
void WifiForwarder::getPollData(std::vector<pollfd>* fds) const {
int fd = mLocalConnection.getFd();
if (fd >= 0) {
struct pollfd pfd = { fd, POLLIN, 0 };
fds->push_back(pfd);
}
fd = mRemoteConnection.getFd();
if (fd >= 0) {
struct pollfd pfd = { fd, POLLIN, 0 };
fds->push_back(pfd);
}
}
Pollable::Timestamp WifiForwarder::getTimeout() const {
if (mRemoteConnection.getFd() == -1) {
return mInitDeadline;
}
return std::min(mLocalConnection.getTimeout(), mDeadline);
}
bool WifiForwarder::onReadAvailable(int fd, int* /*status*/) {
if (fd == mRemoteConnection.getFd()) {
mRemoteConnection.receive();
} else if (fd == mLocalConnection.getFd()) {
mLocalConnection.receive();
}
return true;
}
bool WifiForwarder::onClose(int /*fd*/, int* /*status*/) {
ALOGE("WifiForwarder socket closed unexpectedly");
return false;
}
bool WifiForwarder::onTimeout(Timestamp now, int* /*status*/) {
bool success = true;
if (now >= mInitDeadline) {
Result res = init();
success = res.isSuccess();
if (mRemoteConnection.getFd() == -1) {
// Remote connection not set up by init, try again later
mInitDeadline = now + std::chrono::minutes(1);
}
}
mLocalConnection.onTimeout(now);
if (now >= mDeadline) {
mDeadline += std::chrono::seconds(1);
mAliases.setCurrentTime(now);
mAliases.expireEntries();
}
return success;
}
const char* WifiForwarder::radioTypeToStr(RadioType type) const {
switch (type) {
case RadioType::Unknown:
return "Unknown";
case RadioType::Local:
return "Local";
case RadioType::Remote:
return "Remote";
}
}
void WifiForwarder::onAck(FrameInfo& info, bool success) {
RadioType type = mRadios[info.transmitter()];
if (type == RadioType::Remote) {
if (kDebugTraffic) {
ALOGE("] ACK -] " PRIMAC " [ %" PRIu64 " ] success: %s",
MACARG(info.transmitter()), info.cookie(),
success ? "true" : "false");
}
if (!mRemoteConnection.ackFrame(info, success)) {
ALOGE("WifiForwarder failed to ack remote frame");
}
} else if (type == RadioType::Local) {
if (kDebugTraffic) {
ALOGE("> ACK -> " PRIMAC " [ %" PRIu64 " ] success: %s",
MACARG(info.transmitter()), info.cookie(),
success ? "true" : "false");
}
if (!mLocalConnection.ackFrame(info, success)) {
ALOGE("WifiForwarder failed to ack local frame");
}
} else {
ALOGE("Unknown transmitter in ack: " PRIMAC,
MACARG(info.transmitter()));
}
}
void WifiForwarder::forwardFrame(std::unique_ptr<Frame> frame,
RadioType sourceType) {
if (kDebugTraffic) {
if (!frame->isBeacon() || kDebugBeaconTraffic) {
bool isRemote = sourceType == RadioType::Remote;
ALOGE("%c " PRIMAC " -%c " PRIMAC " %s",
isRemote ? '[' : '<', isRemote ? ']' : '>',
MACARG(frame->source()),
MACARG(frame->destination()),
frame->str().c_str());
}
}
const MacAddress& source = frame->source();
const MacAddress& transmitter = frame->transmitter();
const MacAddress& destination = frame->destination();
auto& currentType = mRadios[transmitter];
if (currentType != RadioType::Unknown && currentType != sourceType) {
ALOGE("Replacing type for MAC " PRIMAC " of type %s with type %s, "
"this might indicate duplicate MACs on different emulators",
MACARG(source), radioTypeToStr(currentType),
radioTypeToStr(sourceType));
}
currentType = sourceType;
mAliases[source] = transmitter;
bool isMulticast = destination.isMulticast();
bool sendOnRemote = isMulticast;
for (const auto& radio : mRadios) {
const MacAddress& radioAddress = radio.first;
RadioType radioType = radio.second;
if (radioAddress == transmitter) {
// Don't send back to the transmitter
continue;
}
if (sourceType == RadioType::Remote && radioType == RadioType::Remote) {
// Don't forward frames back to the remote, the remote will have
// taken care of this.
continue;
}
bool forward = false;
if (isMulticast || destination == radioAddress) {
// The frame is either multicast or directly intended for this
// radio. Forward it.
forward = true;
} else {
auto alias = mAliases.find(destination);
if (alias != mAliases.end() && alias->second == radioAddress) {
// The frame is destined for an address that is a known alias
// for this radio.
forward = true;
}
}
uint32_t seq = 0;
if (forward) {
switch (radioType) {
case RadioType::Unknown:
ALOGE("Attempted to forward frame to unknown radio type");
break;
case RadioType::Local:
if (kDebugTraffic) {
if (!frame->isBeacon() || kDebugBeaconTraffic) {
ALOGE("> " PRIMAC " -> " PRIMAC " %s",
MACARG(frame->source()),
MACARG(frame->destination()),
frame->str().c_str());
}
}
if (isMulticast) {
// Clone the frame, it might be reused
seq = mLocalConnection.cloneFrame(*frame, radioAddress);
} else {
// This frame has a specific destination, move it
seq = mLocalConnection.transferFrame(std::move(frame),
radioAddress);
// Return so that we don't accidentally reuse the frame.
// This should be safe now because it's a unicast frame
// so it should not be sent to multiple radios.
return;
}
break;
case RadioType::Remote:
sendOnRemote = true;
break;
}
}
}
if (sendOnRemote && sourceType != RadioType::Remote) {
if (kDebugTraffic) {
if (!frame->isBeacon() || kDebugBeaconTraffic) {
ALOGE("] " PRIMAC " -] " PRIMAC " %s",
MACARG(frame->source()),
MACARG(frame->destination()),
frame->str().c_str());
}
}
// This is either a multicast message or destined for a radio known to
// be a remote. No need to send multiple times to a remote, the remote
// will handle that on its own.
mRemoteConnection.sendFrame(std::move(frame));
}
}