Squashed commit of the following:
commit 860a83abce314a6b6622ee5ce00947585a9b9abb
Author: Andreas Huber <andih@google.com>
Date: Thu Mar 1 10:00:19 2018 -0800
Fix out-of-bounds memory read reported by ASAN.
Change-Id: I39caec18a35e36864c258338b1a654ddad07b555
commit 246bd8ed19a817fe06d55bdf543ae8abb6a80327
Author: Andreas Huber <andih@google.com>
Date: Fri Feb 23 11:26:36 2018 -0800
Fix wifi_relay makefile.
Change-Id: I7fa9bc13bd74a8001422c0f21dd97c0b46165e0d
commit 3499b50e2bda379fe5e66e38915577d0b6be9247
Author: Andreas Huber <andih@google.com>
Date: Fri Feb 23 10:55:34 2018 -0800
std::shared_ptr<vsoc::wifi::WifiExchangeView> => vsoc::wifi::WifiExchangeView*
Change-Id: I0da7210dac64990020a2c09ffae3f27be489f17e
commit 15a656d5d98cffe6f829287fe2608b58992cbd03
Author: Andreas Huber <andih@google.com>
Date: Thu Feb 22 14:20:37 2018 -0800
Common guest/host wifi_relay binary, host wifi_relay launched by
launch_cvd.
Change-Id: Ifa5e7bad84df605213519bd76a655b13a55d535d
commit a0d702f8a2de28e98c62361bc52c229d597781a4
Author: Andreas Huber <andih@google.com>
Date: Thu Feb 22 11:45:46 2018 -0800
Now using the new CMD_SUBSCRIBE method on the host.
Change-Id: Ib4800b853d37d2f027ed95b1787789420b5562b8
commit 94c9311b10439964fdf205b61356f1ee646b24ae
Author: Andreas Huber <andih@google.com>
Date: Tue Feb 20 13:31:38 2018 -0800
No more hardcoded MAC addresses.
Change-Id: I10f0d6b5c61225e25a731feb4271fa7032a2c5aa
commit 1701d4ccda395540d88e0e137f1dc204b7636c8a
Author: Andreas Huber <andih@google.com>
Date: Tue Feb 20 11:03:28 2018 -0800
Added copyright headers, removed debugging code, switched to glog.
Change-Id: Ic98e607a8988983441db73617dc9a8599bac2eba
commit b70200a808cd903b0b6a028156684747a7a4e50e
Author: Andreas Huber <andih@google.com>
Date: Tue Feb 20 09:17:40 2018 -0800
Removed inject, attempt at cleaning up after disconnected clients.
Change-Id: I4782d348e7079e8aa12d2789f3fb4bbb1aefd4a4
commit 13fccaa33195832a471d2171afb95bc19fb67446
Author: Andreas Huber <andih@google.com>
Date: Fri Feb 16 14:16:59 2018 -0800
Now things appear to be working more stably.
Change-Id: Ie3bac0bca89c9d8f893825ff0156ab5109c65847
commit d11e2856d93525ac0755b532307a39ec4897fdda
Author: Andreas Huber <andih@google.com>
Date: Thu Feb 15 16:34:30 2018 -0800
separate "inject" into wifi_relay_guest and wifi_relay_host
Change-Id: I255fb9e4f44c31a32f364cbdfa55a0b73b8191e1
commit 7efbd60cb37ab608d25d3a43e7aa6576a8e3c9a6
Author: Andreas Huber <andih@google.com>
Date: Wed Feb 14 11:46:03 2018 -0800
initial attempt to support wifi through mac80211_hwsim
Change-Id: I1ac1440dde40605023f873c4c88401e15100bacc
BUG: 62789500
Test: Later
Change-Id: I53a4c0658a5491a99f34fcc7066f17e0961b1fec
diff --git a/common/commands/Android.bp b/common/commands/Android.bp
index 70311b6..b674475 100644
--- a/common/commands/Android.bp
+++ b/common/commands/Android.bp
@@ -15,4 +15,5 @@
subdirs = [
"wificlient",
+ "wifi_relay",
]
diff --git a/common/commands/wifi_relay/Android.bp b/common/commands/wifi_relay/Android.bp
new file mode 100644
index 0000000..ab85c07
--- /dev/null
+++ b/common/commands/wifi_relay/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2018 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.
+//
+cc_binary {
+ name: "wifi_relay",
+ srcs: [
+ "wifi_relay.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "vsoc_lib",
+ "libcuttlefish_fs",
+ "cuttlefish_auto_resources",
+ "liblog",
+ "libnl",
+ ],
+ static_libs: [
+ "libgflags",
+ "libcuttlefish_wifi_relay",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ target: {
+ linux_glibc: {
+ static_libs: [
+ "libcuttlefish_wifi",
+ "libcuttlefish_host_config",
+ ],
+ },
+ android: {
+ static_libs: [
+ "libcuttlefish_wifi",
+ ],
+ }
+ },
+ defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"]
+}
+
diff --git a/common/commands/wifi_relay/wifi_relay.cpp b/common/commands/wifi_relay/wifi_relay.cpp
new file mode 100644
index 0000000..1af95d9
--- /dev/null
+++ b/common/commands/wifi_relay/wifi_relay.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2018 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_relay.h"
+
+#include "common/libs/wifi_relay/mac80211_hwsim_driver.h"
+#include "common/libs/wifi/nl_client.h"
+
+#if defined(CUTTLEFISH_HOST)
+#include "host/libs/config/host_config.h"
+#endif
+
+#include <linux/netdevice.h>
+#include <linux/nl80211.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+
+#include <gflags/gflags.h>
+#include <glog/logging.h>
+
+#include <fstream>
+
+DEFINE_string(
+ guest_mac_address,
+ "00:43:56:44:80:02",
+ "MAC address of the wifi interface to be created on the guest.");
+
+DEFINE_string(
+ host_mac_address,
+ "42:00:00:00:00:00",
+ "MAC address of the wifi interface running on the host.");
+
+#if !defined(CUTTLEFISH_HOST)
+DEFINE_string(
+ iface_name, "wlan0", "Name of the wifi interface to be created.");
+#endif
+
+WifiRelay::WifiRelay(
+ const Mac80211HwSim::MacAddress &localMAC,
+ const Mac80211HwSim::MacAddress &remoteMAC)
+ : mMac80211HwSim(new Mac80211HwSim(localMAC)) {
+ init_check_ = mMac80211HwSim->initCheck();
+
+ if (init_check_ < 0) {
+ return;
+ }
+
+ init_check_ = mMac80211HwSim->addRemote(
+ remoteMAC,
+#if defined(CUTTLEFISH_HOST)
+ vsoc::wifi::WifiExchangeView::GetInstance(vsoc::GetDomain().c_str())
+#else
+ vsoc::wifi::WifiExchangeView::GetInstance()
+#endif
+ );
+}
+
+int WifiRelay::initCheck() const {
+ return init_check_;
+}
+
+void WifiRelay::run() {
+ for (;;) {
+ fd_set rs;
+ FD_ZERO(&rs);
+
+ FD_SET(mMac80211HwSim->socketFd(), &rs);
+ int maxFd = mMac80211HwSim->socketFd();
+
+ int res = select(maxFd + 1, &rs, nullptr, nullptr, nullptr);
+ if (res <= 0) {
+ continue;
+ }
+
+ if (FD_ISSET(mMac80211HwSim->socketFd(), &rs)) {
+ mMac80211HwSim->handlePacket();
+ }
+ }
+}
+
+int WifiRelay::mac80211Family() const {
+ return mMac80211HwSim->mac80211Family();
+}
+
+int WifiRelay::nl80211Family() const {
+ return mMac80211HwSim->nl80211Family();
+}
+
+int createRadio(cvd::NlClient *nl, int familyMAC80211, const char *phyName) {
+ cvd::Cmd msg;
+ genlmsg_put(
+ msg.Msg(),
+ NL_AUTO_PID,
+ NL_AUTO_SEQ,
+ familyMAC80211,
+ 0,
+ NLM_F_REQUEST,
+ HWSIM_CMD_NEW_RADIO,
+ cvd::kWifiSimVersion);
+
+ nla_put_string(msg.Msg(), HWSIM_ATTR_RADIO_NAME, phyName);
+ nla_put_flag(msg.Msg(), HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE);
+
+ nl->Send(&msg);
+
+ // Responses() pauses until netlink responds to previously sent message.
+ for (auto *r : msg.Responses()) {
+ auto hdr = nlmsg_hdr(r);
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+ return err->error;
+ }
+ }
+
+ return -1;
+}
+
+int getPhyIndex(const std::string &phyName) {
+ std::ifstream file("/sys/class/ieee80211/" + phyName + "/index");
+
+ int number;
+ file >> number;
+
+ return number;
+}
+
+int getInterfaceIndex(cvd::NlClient *nl, int familyNL80211, uint32_t phyIndex) {
+ cvd::Cmd msg;
+ genlmsg_put(
+ msg.Msg(),
+ NL_AUTO_PID,
+ NL_AUTO_SEQ,
+ familyNL80211,
+ 0,
+ NLM_F_REQUEST | NLM_F_DUMP,
+ NL80211_CMD_GET_INTERFACE,
+ 0);
+
+ nl->Send(&msg);
+
+ // Responses() pauses until netlink responds to previously sent message.
+ for (auto *r : msg.Responses()) {
+ auto hdr = nlmsg_hdr(r);
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+ return err->error;
+ }
+
+ // Last message in entire series.
+ if (hdr->nlmsg_type == NLMSG_DONE) {
+ break;
+ }
+
+ // !DONE && !ERROR => content.
+ // Decode attributes supplied by netlink.
+ // the genlmsg_parse puts each attribute in a respective slot in an array,
+ // so we have to preallocate enough space.
+ struct nlattr* attrs[NL80211_ATTR_MAX + 1];
+ auto err = genlmsg_parse(hdr, 0, attrs, NL80211_ATTR_MAX, nullptr);
+
+ // Return error if response could not be parsed. This is actually quite
+ // serious.
+ if (err < 0) {
+ LOG(ERROR) << "Could not process netlink response: " << strerror(-err);
+ return err;
+ }
+
+ // Check if we have WIPHY attribute in response -- and if it's the relevant
+ // one.
+ auto wiphy = attrs[NL80211_ATTR_WIPHY];
+ if (wiphy != nullptr && nla_get_u32(wiphy) == phyIndex) {
+ auto number = attrs[NL80211_ATTR_IFINDEX];
+
+ if (number != nullptr) {
+ return nla_get_u32(number);
+ }
+ }
+ }
+
+ return -1;
+}
+
+int updateInterface(
+ cvd::NlClient *nlRoute,
+ int ifaceIndex,
+ const std::string &name,
+ const uint8_t *mac) {
+ cvd::Cmd msg;
+
+ ifinfomsg ifm{};
+ ifm.ifi_index = ifaceIndex;
+
+ nlmsg_put(
+ msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, RTM_SETLINK, 0, NLM_F_REQUEST);
+
+ nlmsg_append(msg.Msg(), &ifm, sizeof(ifm), 0);
+ nla_put_string(msg.Msg(), IFLA_IFNAME, name.c_str());
+
+ std::vector<uint8_t> macCopy(MAX_ADDR_LEN);
+ memcpy(&macCopy[0], mac, ETH_ALEN);
+
+ nla_put(msg.Msg(), IFLA_ADDRESS, MAX_ADDR_LEN, &macCopy[0]);
+
+ nlRoute->Send(&msg);
+
+ // Responses() pauses until netlink responds to previously sent message.
+ for (auto *r : msg.Responses()) {
+ auto hdr = nlmsg_hdr(r);
+ LOG(VERBOSE) << "got response of type " << hdr->nlmsg_type;
+
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+
+ if (err->error < 0) {
+ LOG(ERROR) << "updateInterface failed w/ " << err->error
+ << " (" << strerror(-err->error) << ")";
+ }
+
+ return err->error;
+ }
+ }
+
+ LOG(VERBOSE) << "No more responses";
+
+ return -1;
+}
+
+int main(int argc, char **argv) {
+ gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+ Mac80211HwSim::MacAddress guestMAC, hostMAC;
+ if (!Mac80211HwSim::ParseMACAddress(FLAGS_guest_mac_address, &guestMAC)
+ || !Mac80211HwSim::ParseMACAddress(FLAGS_host_mac_address, &hostMAC)) {
+ exit(1);
+ }
+
+#ifdef CUTTLEFISH_HOST
+ WifiRelay relay(hostMAC, guestMAC);
+#else
+ WifiRelay relay(guestMAC, hostMAC);
+#endif
+ int res = relay.initCheck();
+
+ if (res < 0) {
+ LOG(ERROR)
+ << "WifiRelay::initCheck() returned error "
+ << res
+ << " ("
+ << strerror(-res)
+ << ")";
+
+ exit(1);
+ }
+
+#if !defined(CUTTLEFISH_HOST)
+ cvd::NlClient client(NETLINK_GENERIC);
+ if (!client.Init()) {
+ LOG(ERROR) << "Could not open Netlink Generic.";
+ exit(1);
+ }
+
+ cvd::NlClient nlRoute(NETLINK_ROUTE);
+ if (!nlRoute.Init()) {
+ LOG(ERROR) << "Could not open Netlink Route.";
+ exit(1);
+ }
+
+ std::unique_ptr<std::thread> nlThread(
+ new std::thread([&client, &nlRoute]() {
+ for (;;) {
+ fd_set rs;
+ FD_ZERO(&rs);
+
+ int fdGeneric = nl_socket_get_fd(client.Sock());
+ int fdRoute = nl_socket_get_fd(nlRoute.Sock());
+
+ FD_SET(fdGeneric, &rs);
+ FD_SET(fdRoute, &rs);
+
+ int maxFd = std::max(fdGeneric, fdRoute);
+
+ int res = select(maxFd + 1, &rs, nullptr, nullptr, nullptr);
+
+ if (res == 0) {
+ continue;
+ } else if (res < 0) {
+ continue;
+ }
+
+ if (FD_ISSET(fdGeneric, &rs)) {
+ nl_recvmsgs_default(client.Sock());
+ }
+
+ if (FD_ISSET(fdRoute, &rs)) {
+ nl_recvmsgs_default(nlRoute.Sock());
+ }
+ }
+ }));
+
+ const std::string phyName = FLAGS_iface_name + "_phy";
+ if (createRadio(&client, relay.mac80211Family(), phyName.c_str()) < 0) {
+ LOG(ERROR) << "Could not create radio.";
+ exit(1);
+ }
+
+ int phyIndex = getPhyIndex(phyName);
+ CHECK_GE(phyIndex, 0);
+ LOG(VERBOSE) << "Got PHY index " << phyIndex;
+
+ int ifaceIndex = getInterfaceIndex(
+ &client, relay.nl80211Family(), static_cast<uint32_t>(phyIndex));
+
+ CHECK_GE(ifaceIndex, 0);
+ LOG(VERBOSE) << "Got interface index " << ifaceIndex;
+
+ if (updateInterface(
+ &nlRoute, ifaceIndex, FLAGS_iface_name, &guestMAC[0]) < 0) {
+ LOG(ERROR) << "Failed to update interface.";
+ exit(1);
+ }
+#endif // !defined(CUTTLEFISH_HOST)
+
+ relay.run();
+
+ return 0;
+}
diff --git a/common/commands/wifi_relay/wifi_relay.h b/common/commands/wifi_relay/wifi_relay.h
new file mode 100644
index 0000000..98dce3a
--- /dev/null
+++ b/common/commands/wifi_relay/wifi_relay.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include "common/libs/wifi_relay/mac80211_hwsim.h"
+
+#include <errno.h>
+#include <memory>
+
+class WifiRelay {
+ public:
+ WifiRelay(
+ const Mac80211HwSim::MacAddress &localMAC,
+ const Mac80211HwSim::MacAddress &remoteMAC);
+
+ WifiRelay(const WifiRelay &) = delete;
+ WifiRelay &operator=(const WifiRelay &) = delete;
+
+ virtual ~WifiRelay() = default;
+
+ int initCheck() const;
+
+ void run();
+
+ int mac80211Family() const;
+ int nl80211Family() const;
+
+ private:
+ int init_check_ = -ENODEV;
+
+ std::unique_ptr<Mac80211HwSim> mMac80211HwSim;
+};
+
diff --git a/common/libs/Android.bp b/common/libs/Android.bp
index 648d657..b23571f 100644
--- a/common/libs/Android.bp
+++ b/common/libs/Android.bp
@@ -21,4 +21,5 @@
"threads",
"time",
"wifi",
+ "wifi_relay",
]
diff --git a/common/libs/wifi_relay/Android.bp b/common/libs/wifi_relay/Android.bp
new file mode 100644
index 0000000..8680364
--- /dev/null
+++ b/common/libs/wifi_relay/Android.bp
@@ -0,0 +1,24 @@
+cc_library_static {
+ name: "libcuttlefish_wifi_relay",
+
+ srcs: [
+ "mac80211_hwsim.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libnl",
+ "vsoc_lib",
+ ],
+ header_libs: [
+ "cuttlefish_glog",
+ ],
+ target: {
+ linux_glibc: {
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libgflags",
+ ],
+ }
+ },
+ defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"]
+}
diff --git a/common/libs/wifi_relay/mac80211_hwsim.cpp b/common/libs/wifi_relay/mac80211_hwsim.cpp
new file mode 100644
index 0000000..c08fcc9
--- /dev/null
+++ b/common/libs/wifi_relay/mac80211_hwsim.cpp
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2018 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 "common/libs/wifi_relay/mac80211_hwsim.h"
+
+#include "common/libs/wifi_relay/mac80211_hwsim_driver.h"
+
+#include <glog/logging.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/genl.h>
+#include <signal.h>
+#include <string>
+
+static constexpr char kWifiSimFamilyName[] = "MAC80211_HWSIM";
+static constexpr char kNl80211FamilyName[] = "nl80211";
+
+static constexpr uint32_t kSignalLevelDefault = -24;
+
+#if !defined(ETH_ALEN)
+static constexpr size_t ETH_ALEN = 6;
+#endif
+
+Mac80211HwSim::Remote::Remote(
+ Mac80211HwSim *parent,
+ vsoc::wifi::WifiExchangeView *wifiExchange)
+ : mParent(parent),
+ mWifiExchange(wifiExchange) {
+ mWifiWorker = mWifiExchange->StartWorker();
+
+ mThread.reset(new std::thread([this]{
+ std::unique_ptr<uint8_t[]> buf(
+ new uint8_t[Mac80211HwSim::kMessageSizeMax]);
+
+ for (;;) {
+ intptr_t res =
+ mWifiExchange->Recv(buf.get(), Mac80211HwSim::kMessageSizeMax);
+
+ if (res < 0) {
+ LOG(ERROR) << "WifiExchangeView::Recv failed w/ res " << res;
+ continue;
+ }
+
+ // LOG(INFO) << "GUEST->HOST packet of size " << res;
+
+ struct nlmsghdr *hdr = reinterpret_cast<struct nlmsghdr *>(buf.get());
+
+ int len = res;
+ while (nlmsg_ok(hdr, len)) {
+ mParent->injectMessage(hdr);
+
+ hdr = nlmsg_next(hdr, &len);
+ }
+ }}));
+}
+
+Mac80211HwSim::Remote::~Remote() {
+ mDone = true;
+ mWifiExchange->InterruptSelf();
+
+ mThread->join();
+ mThread.reset();
+}
+
+intptr_t Mac80211HwSim::Remote::send(const void *data, size_t size) {
+ return mWifiExchange->Send(data, size);
+}
+
+Mac80211HwSim::Mac80211HwSim(const MacAddress &mac)
+ : mMAC(mac),
+ mSock(nullptr, nl_socket_free) {
+ int res;
+
+ mSock.reset(nl_socket_alloc());
+
+ if (mSock == nullptr) {
+ goto bail;
+ }
+
+ res = nl_connect(mSock.get(), NETLINK_GENERIC);
+ if (res < 0) {
+ LOG(ERROR) << "nl_connect failed (" << nl_geterror(res) << ")";
+ mInitCheck = res;
+ goto bail;
+ }
+
+ nl_socket_disable_seq_check(mSock.get());
+
+ res = nl_socket_set_buffer_size(
+ mSock.get(), kMessageSizeMax, kMessageSizeMax);
+
+ if (res < 0) {
+ LOG(ERROR)
+ << "nl_socket_set_buffer_size failed ("
+ << nl_geterror(res)
+ << ")";
+
+ mInitCheck = res;
+ goto bail;
+ }
+
+ mMac80211Family = genl_ctrl_resolve(mSock.get(), kWifiSimFamilyName);
+ if (mMac80211Family <= 0) {
+ LOG(ERROR) << "genl_ctrl_resolve failed.";
+ mInitCheck = -ENODEV;
+ goto bail;
+ }
+
+ mNl80211Family = genl_ctrl_resolve(mSock.get(), kNl80211FamilyName);
+ if (mNl80211Family <= 0) {
+ LOG(ERROR) << "genl_ctrl_resolve failed.";
+ mInitCheck = -ENODEV;
+ goto bail;
+ }
+
+#if !defined(CUTTLEFISH_HOST)
+ res = registerOrSubscribe(mMAC);
+
+ if (res < 0) {
+ mInitCheck = res;
+ goto bail;
+ }
+#endif
+
+ mInitCheck = 0;
+ return;
+
+bail:
+ ;
+}
+
+int Mac80211HwSim::initCheck() const {
+ return mInitCheck;
+}
+
+int Mac80211HwSim::socketFd() const {
+ return nl_socket_get_fd(mSock.get());
+}
+
+void Mac80211HwSim::dumpMessage(nlmsghdr *msg) const {
+ genlmsghdr *hdr = genlmsg_hdr(msg);
+
+ LOG(VERBOSE) << "message cmd = " << (int)hdr->cmd;
+
+ nlattr *attrs[__HWSIM_ATTR_MAX + 1];
+ int res = genlmsg_parse(
+ msg,
+ 0 /* hdrlen */,
+ attrs,
+ __HWSIM_ATTR_MAX,
+ nullptr /* policy */);
+
+ if (res < 0) {
+ LOG(ERROR) << "genlmsg_parse failed.";
+ return;
+ }
+
+ // for HWSIM_CMD_FRAME, the following attributes are present:
+ // HWSIM_ATTR_ADDR_TRANSMITTER, HWSIM_ATTR_FRAME, HWSIM_ATTR_FLAGS,
+ // HWSIM_ATTR_TX_INFO, HWSIM_ATTR_COOKIE, HWSIM_ATTR_FREQ
+
+ for (size_t i = 0; i < __HWSIM_ATTR_MAX; ++i) {
+ if (attrs[i]) {
+ LOG(VERBOSE) << "Got attribute " << i;
+ }
+ }
+}
+
+void Mac80211HwSim::injectMessage(nlmsghdr *msg) {
+#ifdef CUTTLEFISH_HOST
+ LOG(VERBOSE) << "------------------- Guest -> Host -----------------------";
+#else
+ LOG(VERBOSE) << "------------------- Host -> Guest -----------------------";
+#endif
+ dumpMessage(msg);
+
+ // Do NOT check nlmsg_type against mMac80211Family, these are dynamically
+ // assigned and may not necessarily match across machines!
+
+ genlmsghdr *hdr = genlmsg_hdr(msg);
+ if (hdr->cmd != HWSIM_CMD_FRAME) {
+ LOG(VERBOSE) << "injectMessage: not cmd HWSIM_CMD_FRAME.";
+ return;
+ }
+
+ nlattr *attrs[__HWSIM_ATTR_MAX + 1];
+ int res = genlmsg_parse(
+ msg,
+ 0 /* hdrlen */,
+ attrs,
+ __HWSIM_ATTR_MAX,
+ nullptr /* policy */);
+
+ if (res < 0) {
+ LOG(ERROR) << "genlmsg_parse failed.";
+ return;
+ }
+
+ nlattr *attr = attrs[HWSIM_ATTR_FRAME];
+ if (attr) {
+ injectFrame(nla_data(attr), nla_len(attr));
+ } else {
+ LOG(ERROR) << "injectMessage: no HWSIM_ATTR_FRAME.";
+ }
+}
+
+void Mac80211HwSim::ackFrame(nlmsghdr *inMsg) {
+ nlattr *attrs[__HWSIM_ATTR_MAX + 1];
+ int res = genlmsg_parse(
+ inMsg,
+ 0 /* hdrlen */,
+ attrs,
+ __HWSIM_ATTR_MAX,
+ nullptr /* policy */);
+
+ if (res < 0) {
+ LOG(ERROR) << "genlmsg_parse failed.";
+ return;
+ }
+
+ uint32_t flags = nla_get_u32(attrs[HWSIM_ATTR_FLAGS]);
+
+ if (!(flags & HWSIM_TX_CTL_REQ_TX_STATUS)) {
+ LOG(VERBOSE) << "Frame doesn't require TX_STATUS.";
+ return;
+ }
+
+ flags |= HWSIM_TX_STAT_ACK;
+
+ const uint8_t *xmitterAddr =
+ static_cast<const uint8_t *>(
+ nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]));
+
+ size_t txRatesLen = nla_len(attrs[HWSIM_ATTR_TX_INFO]);
+
+ const struct hwsim_tx_rate *txRates =
+ static_cast<const struct hwsim_tx_rate *>(
+ nla_data(attrs[HWSIM_ATTR_TX_INFO]));
+
+ uint64_t cookie = nla_get_u64(attrs[HWSIM_ATTR_COOKIE]);
+
+ std::unique_ptr<nl_msg, void (*)(nl_msg *)> outMsg(
+ nlmsg_alloc(), nlmsg_free);
+
+ genlmsg_put(
+ outMsg.get(),
+ NL_AUTO_PID,
+ NL_AUTO_SEQ,
+ mMac80211Family,
+ 0 /* hdrlen */,
+ NLM_F_REQUEST,
+ HWSIM_CMD_TX_INFO_FRAME,
+ 0 /* version */);
+
+ nla_put(outMsg.get(), HWSIM_ATTR_ADDR_TRANSMITTER, ETH_ALEN, xmitterAddr);
+ nla_put_u32(outMsg.get(), HWSIM_ATTR_FLAGS, flags);
+ nla_put_u32(outMsg.get(), HWSIM_ATTR_SIGNAL, kSignalLevelDefault);
+ nla_put(outMsg.get(), HWSIM_ATTR_TX_INFO, txRatesLen, txRates);
+ nla_put_u64(outMsg.get(), HWSIM_ATTR_COOKIE, cookie);
+
+ res = nl_send_auto_complete(mSock.get(), outMsg.get());
+ if (res < 0) {
+ LOG(ERROR) << "Sending TX Info failed. (" << nl_geterror(res) << ")";
+ } else {
+ LOG(VERBOSE) << "Sending TX Info SUCCEEDED.";
+ }
+}
+
+void Mac80211HwSim::injectFrame(const void *data, size_t size) {
+ std::unique_ptr<nl_msg, void (*)(nl_msg *)> msg(nlmsg_alloc(), nlmsg_free);
+
+ genlmsg_put(
+ msg.get(),
+ NL_AUTO_PID,
+ NL_AUTO_SEQ,
+ mMac80211Family,
+ 0 /* hdrlen */,
+ NLM_F_REQUEST,
+ HWSIM_CMD_FRAME,
+ 0 /* version */);
+
+ CHECK_EQ(mMAC.size(), static_cast<size_t>(ETH_ALEN));
+ nla_put(msg.get(), HWSIM_ATTR_ADDR_RECEIVER, ETH_ALEN, &mMAC[0]);
+
+ nla_put(msg.get(), HWSIM_ATTR_FRAME, size, data);
+ nla_put_u32(msg.get(), HWSIM_ATTR_RX_RATE, 1);
+ nla_put_u32(msg.get(), HWSIM_ATTR_SIGNAL, kSignalLevelDefault);
+
+ LOG(VERBOSE) << "INJECTING!";
+ dumpMessage(nlmsg_hdr(msg.get()));
+
+ int res = nl_send_auto_complete(mSock.get(), msg.get());
+
+ if (res < 0) {
+ LOG(ERROR) << "Injection failed. (" << nl_geterror(res) << ")";
+ } else {
+ LOG(VERBOSE) << "Injection SUCCEEDED.";
+ }
+}
+
+void Mac80211HwSim::handlePacket() {
+ sockaddr_nl from;
+ uint8_t *data;
+
+ int len = nl_recv(mSock.get(), &from, &data, nullptr /* creds */);
+ if (len == 0) {
+ LOG(ERROR) << "nl_recv received EOF.";
+ return;
+ } else if (len < 0) {
+ LOG(ERROR) << "nl_recv failed (" << nl_geterror(len) << ")";
+ return;
+ }
+
+ std::unique_ptr<nlmsghdr, void (*)(nlmsghdr *)> msg(
+ reinterpret_cast<nlmsghdr *>(data),
+ [](nlmsghdr *hdr) { free(hdr); });
+
+ if (msg->nlmsg_type != mMac80211Family) {
+ LOG(VERBOSE)
+ << "Received msg of type other than MAC80211: "
+ << msg->nlmsg_type;
+
+ return;
+ }
+
+#ifdef CUTTLEFISH_HOST
+ LOG(VERBOSE) << "------------------- Host -> Guest -----------------------";
+#else
+ LOG(VERBOSE) << "------------------- Guest -> Host -----------------------";
+#endif
+
+ dumpMessage(msg.get());
+
+#if !defined(CUTTLEFISH_HOST)
+ ackFrame(msg.get());
+#endif
+
+ std::lock_guard<std::mutex> autoLock(mRemotesLock);
+ for (auto &remoteEntry : mRemotes) {
+ // TODO(andih): Check which remotes to forward this packet to based
+ // on the destination address.
+ remoteEntry.second->send(msg.get(), msg->nlmsg_len);
+ }
+}
+
+int Mac80211HwSim::registerOrSubscribe(const MacAddress &mac) {
+ std::unique_ptr<nl_msg, void (*)(nl_msg *)> msg(nullptr, nlmsg_free);
+
+ msg.reset(nlmsg_alloc());
+
+ genlmsg_put(
+ msg.get(),
+ NL_AUTO_PID,
+ NL_AUTO_SEQ,
+ mMac80211Family,
+ 0,
+ NLM_F_REQUEST,
+#ifdef CUTTLEFISH_HOST
+ HWSIM_CMD_SUBSCRIBE,
+#else
+ HWSIM_CMD_REGISTER,
+#endif
+ 0);
+
+#ifdef CUTTLEFISH_HOST
+ nla_put(msg.get(), HWSIM_ATTR_ADDR_RECEIVER, ETH_ALEN, &mac[0]);
+#else
+ // HWSIM_CMD_REGISTER is a global command not specific to a MAC.
+ (void)mac;
+#endif
+
+ int res = nl_send_auto_complete(mSock.get(), msg.get());
+
+ if (res < 0) {
+ LOG(ERROR)
+ << "Registration/subscription failed. (" << nl_geterror(res) << ")";
+
+ return res;
+ }
+
+ return 0;
+}
+
+int Mac80211HwSim::addRemote(
+ const MacAddress &mac,
+ vsoc::wifi::WifiExchangeView *wifiExchange) {
+#ifdef CUTTLEFISH_HOST
+ int res = registerOrSubscribe(mac);
+
+ if (res < 0) {
+ return res;
+ }
+#endif
+
+ std::lock_guard<std::mutex> autoLock(mRemotesLock);
+
+ std::unique_ptr<Remote> remote(new Remote(this, wifiExchange));
+ mRemotes.insert(std::make_pair(mac, std::move(remote)));
+
+ return 0;
+}
+
+void Mac80211HwSim::removeRemote(const MacAddress &mac) {
+ std::lock_guard<std::mutex> autoLock(mRemotesLock);
+ auto it = mRemotes.find(mac);
+ if (it != mRemotes.end()) {
+ mRemotes.erase(it);
+ }
+}
+
+// static
+bool Mac80211HwSim::ParseMACAddress(const std::string &s, MacAddress *mac) {
+ mac->resize(ETH_ALEN);
+
+ char dummy;
+ if (sscanf(s.c_str(),
+ "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx%c",
+ &(*mac)[0],
+ &(*mac)[1],
+ &(*mac)[2],
+ &(*mac)[3],
+ &(*mac)[4],
+ &(*mac)[5],
+ &dummy) != 6) {
+ LOG(ERROR) << "Failed to parse MAC address: " << s;
+ return false;
+ }
+
+ return true;
+}
diff --git a/common/libs/wifi_relay/mac80211_hwsim.h b/common/libs/wifi_relay/mac80211_hwsim.h
new file mode 100644
index 0000000..43f94fe
--- /dev/null
+++ b/common/libs/wifi_relay/mac80211_hwsim.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include "common/vsoc/lib/wifi_exchange_view.h"
+
+#include <errno.h>
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <netlink/netlink.h>
+#include <vector>
+
+struct Mac80211HwSim {
+ using MacAddress = std::vector<uint8_t>;
+
+ static constexpr size_t kMessageSizeMax = 128 * 1024;
+
+ explicit Mac80211HwSim(const MacAddress &mac);
+ Mac80211HwSim(const Mac80211HwSim &) = delete;
+ Mac80211HwSim &operator=(const Mac80211HwSim &) = delete;
+
+ virtual ~Mac80211HwSim() = default;
+
+ int initCheck() const;
+
+ int socketFd() const;
+
+ void handlePacket();
+
+ int mac80211Family() const { return mMac80211Family; }
+ int nl80211Family() const { return mNl80211Family; }
+
+ int addRemote(
+ const MacAddress &mac,
+ vsoc::wifi::WifiExchangeView *wifiExchange);
+
+ void removeRemote(const MacAddress &mac);
+
+ static bool ParseMACAddress(const std::string &s, MacAddress *mac);
+
+private:
+ struct Remote {
+ explicit Remote(
+ Mac80211HwSim *parent,
+ vsoc::wifi::WifiExchangeView *wifiExchange);
+
+ Remote(const Remote &) = delete;
+ Remote &operator=(const Remote &) = delete;
+
+ virtual ~Remote();
+
+ intptr_t send(const void *data, size_t size);
+
+ private:
+ Mac80211HwSim *mParent;
+ vsoc::wifi::WifiExchangeView *mWifiExchange;
+ std::unique_ptr<vsoc::RegionWorker> mWifiWorker;
+
+ volatile bool mDone = false;
+ std::unique_ptr<std::thread> mThread;
+ };
+
+ int mInitCheck = -ENODEV;
+ MacAddress mMAC;
+ std::unique_ptr<nl_sock, void (*)(nl_sock *)> mSock;
+ int mMac80211Family = 0;
+ int mNl80211Family = 0;
+
+ std::mutex mRemotesLock;
+ std::map<MacAddress, std::unique_ptr<Remote>> mRemotes;
+
+ void injectMessage(nlmsghdr *hdr);
+ void injectFrame(const void *data, size_t size);
+ void ackFrame(nlmsghdr *msg);
+ void dumpMessage(nlmsghdr *msg) const;
+ int registerOrSubscribe(const MacAddress &mac);
+};
diff --git a/common/libs/wifi_relay/mac80211_hwsim_driver.h b/common/libs/wifi_relay/mac80211_hwsim_driver.h
new file mode 100644
index 0000000..c472254
--- /dev/null
+++ b/common/libs/wifi_relay/mac80211_hwsim_driver.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+enum hwsim_cmd {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ HWSIM_CMD_NEW_RADIO,
+ HWSIM_CMD_DEL_RADIO,
+ HWSIM_CMD_GET_RADIO,
+ HWSIM_CMD_SUBSCRIBE,
+ __HWSIM_CMD_MAX
+};
+
+enum hwsim_attr {
+ /* 0 */ HWSIM_ATTR_UNSPEC,
+ /* 1 */ HWSIM_ATTR_ADDR_RECEIVER,
+ /* 2 */ HWSIM_ATTR_ADDR_TRANSMITTER,
+ /* 3 */ HWSIM_ATTR_FRAME,
+ /* 4 */ HWSIM_ATTR_FLAGS,
+ /* 5 */ HWSIM_ATTR_RX_RATE,
+ /* 6 */ HWSIM_ATTR_SIGNAL,
+ /* 7 */ HWSIM_ATTR_TX_INFO,
+ /* 8 */ HWSIM_ATTR_COOKIE,
+ /* 9 */ HWSIM_ATTR_CHANNELS,
+ /* 10 */ HWSIM_ATTR_RADIO_ID,
+ /* 11 */ HWSIM_ATTR_REG_HINT_ALPHA2,
+ /* 12 */ HWSIM_ATTR_REG_CUSTOM_REG,
+ /* 13 */ HWSIM_ATTR_REG_STRICT_REG,
+ /* 14 */ HWSIM_ATTR_SUPPORT_P2P_DEVICE,
+ /* 15 */ HWSIM_ATTR_USE_CHANCTX,
+ /* 16 */ HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE,
+ /* 17 */ HWSIM_ATTR_RADIO_NAME,
+ /* 18 */ HWSIM_ATTR_NO_VIF,
+ /* 19 */ HWSIM_ATTR_FREQ,
+ __HWSIM_ATTR_MAX
+};
+
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = 1,
+ HWSIM_TX_CTL_NO_ACK = 2,
+ HWSIM_TX_STAT_ACK = 4,
+};
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 053c391..eacd5da 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -124,6 +124,12 @@
"host-side socket_forward_proxy process will bias the port "
"numbers.");
+DEFINE_bool(start_wifi_relay, true, "Whether to start the wifi_relay process.");
+DEFINE_string(wifi_relay_binary,
+ StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
+ "/bin/wifi_relay",
+ "Location of the wifi_relay binary.");
+
DECLARE_string(uuid);
namespace {
@@ -488,5 +494,20 @@
port_options.c_str(), NULL};
subprocess(vnc_command, NULL, false);
}
+
+ if (FLAGS_start_wifi_relay) {
+ // Launch the wifi relay, don't wait for it to complete
+ std::string domainArg = "--domain=" + vsoc::GetDomain();
+
+ const char* relay_command[] = {
+ "/usr/bin/sudo",
+ FLAGS_wifi_relay_binary.c_str(),
+ domainArg.c_str(),
+ NULL
+ };
+
+ subprocess(relay_command, NULL /* envp */, false /* wait_for_child */);
+ }
+
pause();
}