blob: 457a90737a3b0df05163645abdb229aefe522a9f [file] [log] [blame]
/*
* Copyright (C) 2017 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 "host/commands/wifid/virtual_wifi_manager.h"
#include <glog/logging.h>
#include "host/commands/wifid/cmd.h"
#include "host/commands/wifid/mac80211.h"
namespace avd {
namespace {
// We don't care about byte ordering as much as we do about having all bytes
// there. Byte order does not match, we want to use it as a key in our map.
// Note: we accept const void here, because we will also process data coming
// from netlink (which is untyped).
uint64_t MACToKey(const void* macaddr) {
auto typed = reinterpret_cast<const uint16_t*>(macaddr);
return (1ull * typed[0] << 32) | (typed[1] << 16) | typed[2];
}
} // namespace
// Register for asynchronous notifications from MAC80211.
// Our callback will receive data for each next frame transmitted over any
// radio.
bool VirtualWIFIManager::RegisterForSimulatorNotifications() {
Cmd msg;
if (!genlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, nl_->FamilyMAC80211(),
0, NLM_F_REQUEST, HWSIM_CMD_REGISTER, kWifiSimVersion)) {
LOG(ERROR) << "Could not create nlmsg registration request.";
return false;
}
nl_->GeNL().Send(&msg);
for (auto* r : msg.Responses()) {
auto hdr = nlmsg_hdr(r);
if (hdr->nlmsg_type == NLMSG_ERROR) {
nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
LOG_IF(ERROR, err->error < 0)
<< "Could not register for VirtualWIFIManager notifications: "
<< strerror(err->error);
return err->error == 0;
}
}
LOG(ERROR) << "No response from netlink.";
return false;
}
bool VirtualWIFIManager::Init() {
nl_->GeNL().SetDefaultHandler([this](nl_msg* m) { HandleNlResponse(m); });
return RegisterForSimulatorNotifications();
}
VirtualWIFIManager::~VirtualWIFIManager() {
// Reset handler.
nl_->GeNL().SetDefaultHandler(std::function<void(nl_msg*)>());
}
void VirtualWIFIManager::HandleNlResponse(nl_msg* m) {
auto hdr = nlmsg_hdr(m);
auto gen = static_cast<genlmsghdr*>(nlmsg_data(hdr));
// Ignore Generic Netlink messages coming from other sources.
if (hdr->nlmsg_type != nl_->FamilyMAC80211()) return;
// Ignore Generic Netlink messages that don't contain MAC80211 frames.
if (gen->cmd != HWSIM_CMD_FRAME) return;
struct nlattr* attrs[HWSIM_ATTR_MAX + 1];
if (genlmsg_parse(hdr, 0, attrs, HWSIM_ATTR_MAX, nullptr)) return;
// Get virtual wlan key from mac address.
auto mac = attrs[HWSIM_ATTR_ADDR_TRANSMITTER];
if (!mac) return;
auto key = MACToKey(nla_data(mac));
// Redirect packet to VirtualWIFI, if that's indeed one of ours.
// Sadly, we don't have any other way of telling.
std::shared_ptr<VirtualWIFI> wifi;
{
std::lock_guard<std::mutex> lock(radios_mutex_);
auto radio = radios_.find(key);
if (radio == radios_.end()) return;
wifi = radio->second.lock();
}
LOG(INFO) << "Found packet from " << wifi->Name();
}
// Create new MAC80211_HWSIM radio.
// This can be called after Init completes.
std::shared_ptr<VirtualWIFI> VirtualWIFIManager::CreateRadio(
const std::string& name, const std::string& address) {
std::shared_ptr<VirtualWIFI> wifi(new VirtualWIFI(nl_, name, address));
if (!wifi->Init()) {
wifi.reset();
return wifi;
}
std::lock_guard<std::mutex> lock(radios_mutex_);
radios_[MACToKey(wifi->MacAddr())] = wifi;
return wifi;
}
} // namespace avd