Cuttlefish Wifi: Packet switcher
Change-Id: I960a16d0ab13cd2fbfcdfdb148bb696ce379f474
diff --git a/common/commands/wificlient/Android.bp b/common/commands/wificlient/Android.bp
index 067d085..cc2e268 100644
--- a/common/commands/wificlient/Android.bp
+++ b/common/commands/wificlient/Android.bp
@@ -5,7 +5,9 @@
],
shared_libs: [
"libbase",
- "libcuttlefish_wifi",
+ "vsoc_lib",
+ "libcuttlefish_fs",
+ "cuttlefish_auto_resources",
"liblog",
"libnl",
],
@@ -15,5 +17,18 @@
header_libs: [
"cuttlefish_glog",
],
- defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+ target: {
+ linux: {
+ 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/wificlient/main.cc b/common/commands/wificlient/main.cc
index e060408..1b6736b 100644
--- a/common/commands/wificlient/main.cc
+++ b/common/commands/wificlient/main.cc
@@ -18,6 +18,7 @@
#include <glog/logging.h>
#include "common/libs/wifi/netlink.h"
+#include "common/libs/wifi/packet_switch.h"
#include "common/libs/wifi/virtual_wifi.h"
DEFINE_string(router, "cvd-wifirouter", "Path to WIFI Router Unix socket.");
@@ -35,6 +36,12 @@
exit(1);
}
+ cvd::PacketSwitch pktswitch(nl.get());
+ if (!pktswitch.Init()) {
+ LOG(ERROR) << "Could not initialize packet switch.";
+ exit(1);
+ }
+
std::unique_ptr<cvd::VirtualWIFI> radio(
new cvd::VirtualWIFI(nl.get(), FLAGS_iface, FLAGS_macaddr));
if (!radio->Init()) {
@@ -42,5 +49,7 @@
exit(1);
}
+ pktswitch.Start();
+
pause();
}
diff --git a/common/libs/wifi/Android.bp b/common/libs/wifi/Android.bp
index 0ce06f1..6a6e846 100644
--- a/common/libs/wifi/Android.bp
+++ b/common/libs/wifi/Android.bp
@@ -1,10 +1,11 @@
-cc_library_shared {
+cc_library_static {
name: "libcuttlefish_wifi",
srcs: [
+ "cmd.cc",
"netlink.cc",
"nl_client.cc",
- "cmd.cc",
+ "packet_switch.cc",
"virtual_wifi.cc",
"wr_client.cc",
],
@@ -16,5 +17,13 @@
header_libs: [
"cuttlefish_glog",
],
- defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"],
+ target: {
+ linux: {
+ static_libs: [
+ "libcuttlefish_host_config",
+ "libgflags",
+ ],
+ }
+ },
+ defaults: ["cuttlefish_host_and_guest", "cuttlefish_native_isa"]
}
diff --git a/common/libs/wifi/cmd.cc b/common/libs/wifi/cmd.cc
index 1a153cc..2576359 100644
--- a/common/libs/wifi/cmd.cc
+++ b/common/libs/wifi/cmd.cc
@@ -19,6 +19,8 @@
Cmd::Cmd() : msg_(nlmsg_alloc()) {}
+Cmd::Cmd(nlmsghdr* h) : msg_(nlmsg_convert(h)) {}
+
Cmd::~Cmd() {
for (auto& msg : responses_) {
nlmsg_free(msg);
diff --git a/common/libs/wifi/cmd.h b/common/libs/wifi/cmd.h
index f5d9da0..f9ff509 100644
--- a/common/libs/wifi/cmd.h
+++ b/common/libs/wifi/cmd.h
@@ -28,6 +28,7 @@
class Cmd {
public:
Cmd();
+ explicit Cmd(nlmsghdr* h);
~Cmd();
// Cmd() creates netlink request to be sent to kernel.
diff --git a/common/libs/wifi/packet_switch.cc b/common/libs/wifi/packet_switch.cc
new file mode 100644
index 0000000..5dc58fe
--- /dev/null
+++ b/common/libs/wifi/packet_switch.cc
@@ -0,0 +1,108 @@
+/*
+ * 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 "common/libs/wifi/packet_switch.h"
+#include "common/libs/wifi/router.h"
+
+#ifdef CUTTLEFISH_HOST
+#include "host/libs/config/host_config.h"
+#endif
+
+namespace cvd {
+
+PacketSwitch::~PacketSwitch() { Stop(); }
+
+bool PacketSwitch::Init() {
+#ifdef CUTTLEFISH_HOST
+ return shm_wifi_.Open(vsoc::GetDomain().c_str());
+#else
+ return shm_wifi_.Open();
+#endif
+}
+
+void PacketSwitch::Start() {
+ std::lock_guard<std::mutex> l(op_mutex_);
+ if (started_) return;
+ // set started to true immediately; this attribute is referenced by threads to
+ // know whether they should terminate.
+ started_ = true;
+
+ nl_->WRCL().SetDefaultHandler(
+ [this](nl_msg* m) { ProcessPacket(m, false); });
+
+ shm_xchg_.reset(new std::thread([this] {
+ size_t maxlen = getpagesize();
+ std::unique_ptr<uint8_t[]> msg(new uint8_t[maxlen]);
+ while (started_) {
+ // TODO(ender): how to trigger (periodic?) exit from this call?
+ auto len = shm_wifi_.Recv(msg.get(), maxlen);
+ std::unique_ptr<nl_msg, void (*)(nl_msg*)> nlm(
+ nlmsg_convert(reinterpret_cast<nlmsghdr*>(msg.get())), nlmsg_free);
+ ProcessPacket(nlm.get(), true);
+ }
+ }));
+}
+
+void PacketSwitch::Stop() {
+ std::lock_guard<std::mutex> l(op_mutex_);
+ if (!started_) return;
+ started_ = false;
+ nl_->WRCL().SetDefaultHandler(std::function<void(nl_msg*)>());
+
+ shm_xchg_->join();
+ shm_xchg_.reset();
+}
+
+void PacketSwitch::ProcessPacket(nl_msg* m, bool is_incoming) {
+ auto header = nlmsg_hdr(m);
+ auto genhdr = reinterpret_cast<genlmsghdr*>(nlmsg_data(header));
+
+ if (genhdr->cmd == WIFIROUTER_CMD_NOTIFY) {
+ // This attribute is mandatory: it contains MAC80211_HWSIM frame.
+ auto packet =
+ nlmsg_find_attr(header, sizeof(*genhdr), WIFIROUTER_ATTR_PACKET);
+ if (!packet) return;
+
+ // If origin is not local (= not set from local WIFI), then forward it to
+ // local WIFI.
+ if (is_incoming) {
+ // Need to update MAC80211_HWSIM WIFI family before injecting packet.
+ // Different kernels may have different family numbers allocated.
+ auto frame = reinterpret_cast<nlmsghdr*>(nla_data(packet));
+ frame->nlmsg_type = nl_->FamilyMAC80211();
+ frame->nlmsg_pid = 0;
+ frame->nlmsg_seq = 0;
+ frame->nlmsg_flags = NLM_F_REQUEST;
+ Cmd local(frame);
+
+ nl_->GeNL().Send(&local);
+
+ for (auto* r : local.Responses()) {
+ auto hdr = nlmsg_hdr(r);
+ if (hdr->nlmsg_type == NLMSG_ERROR) {
+ nlmsgerr* err = static_cast<nlmsgerr*>(nlmsg_data(hdr));
+ if (err->error < 0) {
+ LOG(ERROR) << "Could not send WIFI message: "
+ << strerror(-err->error);
+ }
+ }
+ }
+ } else {
+ shm_wifi_.Send(&header, header->nlmsg_len);
+ }
+ }
+}
+
+} // namespace cvd
diff --git a/common/libs/wifi/packet_switch.h b/common/libs/wifi/packet_switch.h
new file mode 100644
index 0000000..c20e681
--- /dev/null
+++ b/common/libs/wifi/packet_switch.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <memory>
+#include <set>
+
+#include "common/libs/wifi/netlink.h"
+#include "common/libs/wifi/virtual_wifi.h"
+#include "common/libs/wifi/wr_client.h"
+#include "common/vsoc/lib/wifi_exchange_view.h"
+
+namespace cvd {
+
+class PacketSwitch {
+ public:
+ PacketSwitch(Netlink* nl) : nl_(nl) {}
+ ~PacketSwitch();
+
+ bool Init();
+ void Start();
+ void Stop();
+
+ private:
+ void ProcessPacket(nl_msg* m, bool is_incoming);
+
+ Netlink* nl_;
+
+ std::mutex op_mutex_;
+ // Started is referenced by all threads created by PacketSwitch to determine
+ // whether to carry on working, or terminate.
+ bool started_;
+
+ std::unique_ptr<std::thread> shm_xchg_;
+ vsoc::wifi::WifiExchangeView shm_wifi_;
+
+ PacketSwitch(const PacketSwitch&) = delete;
+ PacketSwitch& operator=(const PacketSwitch&) = delete;
+};
+
+} // namespace cvd
diff --git a/common/vsoc/lib/wifi_exchange_view.cpp b/common/vsoc/lib/wifi_exchange_view.cpp
index 23e0bc6..301f35f 100644
--- a/common/vsoc/lib/wifi_exchange_view.cpp
+++ b/common/vsoc/lib/wifi_exchange_view.cpp
@@ -20,17 +20,17 @@
namespace vsoc {
namespace wifi {
-bool WifiExchangeView::Send(const void* buffer, size_t length) {
+intptr_t WifiExchangeView::Send(const void* buffer, intptr_t length) {
#ifdef CUTTLEFISH_HOST
return data()->guest_ingress.Write(this, static_cast<const char*>(buffer),
- length) == length;
+ length);
#else
return data()->guest_egress.Write(this, static_cast<const char*>(buffer),
- length) == length;
+ length);
#endif
}
-intptr_t WifiExchangeView::Recv(void* buffer, size_t max_length) {
+intptr_t WifiExchangeView::Recv(void* buffer, intptr_t max_length) {
#ifdef CUTTLEFISH_HOST
return data()->guest_egress.Read(this, static_cast<char*>(buffer),
max_length);
diff --git a/common/vsoc/lib/wifi_exchange_view.h b/common/vsoc/lib/wifi_exchange_view.h
index 83d7e6a..6bdab03 100644
--- a/common/vsoc/lib/wifi_exchange_view.h
+++ b/common/vsoc/lib/wifi_exchange_view.h
@@ -27,11 +27,11 @@
public:
// Send netlink packet to peer.
// returns true, if operation was successful.
- bool Send(const void* buffer, size_t length);
+ intptr_t Send(const void* buffer, intptr_t length);
// Receive netlink packet from peer.
// Returns number of bytes read, or negative value, if failed.
- intptr_t Recv(void* buffer, size_t max_length);
+ intptr_t Recv(void* buffer, intptr_t max_length);
// Set guest MAC address.
void SetGuestMACAddress(const uint8_t* mac_address);