WIFIRouter coop for Cuttlefish Wifi
Change-Id: I58afda9f9ec80ba91c84f4a9dadefa2df386d100
diff --git a/common/libs/wifi/Android.bp b/common/libs/wifi/Android.bp
index a70531e..f6e68ef 100644
--- a/common/libs/wifi/Android.bp
+++ b/common/libs/wifi/Android.bp
@@ -1,4 +1,4 @@
-cc_library {
+cc_library_static {
name: "libcuttlefish_wifi",
host_supported: true,
@@ -7,7 +7,7 @@
"nl_client.cc",
"cmd.cc",
"virtual_wifi.cc",
- "virtual_wifi_manager.cc",
+ "wr_client.cc",
],
static_libs: [
"libbase",
diff --git a/common/libs/wifi/cmd.cc b/common/libs/wifi/cmd.cc
index 762e7fb..1a153cc 100644
--- a/common/libs/wifi/cmd.cc
+++ b/common/libs/wifi/cmd.cc
@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "host/commands/wifid/cmd.h"
+#include "common/libs/wifi/cmd.h"
-namespace avd {
+namespace cvd {
Cmd::Cmd() : msg_(nlmsg_alloc()) {}
@@ -60,4 +60,4 @@
return responses_;
}
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/cmd.h b/common/libs/wifi/cmd.h
index c4bc57b..f5d9da0 100644
--- a/common/libs/wifi/cmd.h
+++ b/common/libs/wifi/cmd.h
@@ -22,7 +22,7 @@
#include <netlink/msg.h>
-namespace avd {
+namespace cvd {
constexpr int kWifiSimVersion = 1;
class Cmd {
@@ -55,4 +55,4 @@
Cmd& operator=(const Cmd&) = delete;
};
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/netlink.cc b/common/libs/wifi/netlink.cc
index b1fd7de..143a585 100644
--- a/common/libs/wifi/netlink.cc
+++ b/common/libs/wifi/netlink.cc
@@ -13,19 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "host/commands/wifid/netlink.h"
+#include "common/libs/wifi/netlink.h"
#include <glog/logging.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/family.h>
-namespace avd {
+namespace cvd {
namespace {
constexpr char kWifiSimFamilyName[] = "MAC80211_HWSIM";
constexpr char kNl80211FamilyName[] = "nl80211";
} // namespace
-Netlink::Netlink() : genl_(NETLINK_GENERIC), rtnl_(NETLINK_ROUTE) {}
+Netlink::Netlink(const std::string& wifirouter_socket)
+ : genl_(NETLINK_GENERIC), rtnl_(NETLINK_ROUTE), wrcl_(wifirouter_socket) {}
bool Netlink::Init() {
if (!genl_.Init()) {
@@ -38,6 +39,11 @@
return false;
}
+ if (!wrcl_.Init()) {
+ LOG(ERROR) << "Could not connect to Wifi Router.";
+ return false;
+ }
+
// Start the thread processing asynchronous netlink responses.
netlink_thread_.reset(new std::thread([this]() { HandleNetlinkMessages(); }));
@@ -68,19 +74,22 @@
fd_set nlfds;
int genl_fd = nl_socket_get_fd(GeNL().Sock());
int rtnl_fd = nl_socket_get_fd(RtNL().Sock());
- int max_fd = std::max(genl_fd, rtnl_fd) + 1;
+ int wrcl_fd = wrcl_.Sock();
+ int max_fd = std::max({genl_fd, rtnl_fd, wrcl_fd}) + 1;
while (true) {
FD_ZERO(&nlfds);
FD_SET(genl_fd, &nlfds);
FD_SET(rtnl_fd, &nlfds);
+ FD_SET(wrcl_fd, &nlfds);
int res = select(max_fd, &nlfds, nullptr, nullptr, nullptr);
if (res <= 0) continue;
if (FD_ISSET(genl_fd, &nlfds)) nl_recvmsgs_default(GeNL().Sock());
if (FD_ISSET(rtnl_fd, &nlfds)) nl_recvmsgs_default(RtNL().Sock());
+ if (FD_ISSET(wrcl_fd, &nlfds)) wrcl_.HandleResponses();
}
}
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/netlink.h b/common/libs/wifi/netlink.h
index 7d8ac8c..20b13c1 100644
--- a/common/libs/wifi/netlink.h
+++ b/common/libs/wifi/netlink.h
@@ -19,13 +19,14 @@
#include <thread>
#include <netlink/genl/genl.h>
-#include "host/commands/wifid/nl_client.h"
+#include "common/libs/wifi/nl_client.h"
+#include "common/libs/wifi/wr_client.h"
-namespace avd {
+namespace cvd {
// Netlink provides access to relevant netlink backends and resources.
class Netlink {
public:
- Netlink();
+ Netlink(const std::string& wifirouter_socket);
~Netlink() = default;
// Initialize instance of Netlink Factory.
@@ -37,6 +38,8 @@
// Getter for NETLINK_ROUTE NlClient instance.
NlClient& RtNL() { return rtnl_; }
+ WRClient& WRCL() { return wrcl_; }
+
// Access Family ID for MAC80211 (WIFI Simulator).
int FamilyMAC80211() const { return mac80211_hwsim_family_; }
@@ -51,8 +54,10 @@
NlClient genl_;
NlClient rtnl_;
+ WRClient wrcl_;
int mac80211_hwsim_family_ = 0;
+ int router_family_ = 0;
int nl80211_family_ = 0;
std::unique_ptr<std::thread> netlink_thread_;
@@ -61,4 +66,4 @@
Netlink& operator=(const Netlink&) = delete;
};
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/nl_client.cc b/common/libs/wifi/nl_client.cc
index 436631b..5588e3d 100644
--- a/common/libs/wifi/nl_client.cc
+++ b/common/libs/wifi/nl_client.cc
@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#include "host/commands/wifid/nl_client.h"
+#include "common/libs/wifi/nl_client.h"
#include <glog/logging.h>
-namespace avd {
+namespace cvd {
NlClient::NlClient(int nl_type)
: nl_type_(nl_type),
@@ -92,4 +92,4 @@
nl_sock* NlClient::Sock() const { return sock_.get(); }
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/nl_client.h b/common/libs/wifi/nl_client.h
index 556f404..2b5a57f 100644
--- a/common/libs/wifi/nl_client.h
+++ b/common/libs/wifi/nl_client.h
@@ -22,9 +22,9 @@
#include <netlink/genl/genl.h>
-#include "host/commands/wifid/cmd.h"
+#include "common/libs/wifi/cmd.h"
-namespace avd {
+namespace cvd {
class NlClient {
public:
@@ -63,4 +63,4 @@
NlClient& operator=(const NlClient&) = delete;
};
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/router.h b/common/libs/wifi/router.h
new file mode 100644
index 0000000..9a5ab93
--- /dev/null
+++ b/common/libs/wifi/router.h
@@ -0,0 +1,51 @@
+/*
+ * 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
+
+namespace cvd {
+// Commands recognized by WIFIRouter netlink family.
+enum {
+ // WIFIROUTER_CMD_REGISTER is used by client to request notifications for
+ // packets sent from an interface with specific MAC address. Recognized
+ // attributes:
+ // - WIFIROUTER_ATTR_MAC - MAC address (byte array) of interface to receive
+ // notifications for.
+ WIFIROUTER_CMD_REGISTER,
+
+ // WIFIROUTER_CMD_NOTIFY is issued by the server to notify clients for every
+ // new WIFIROUTER packet the client is registered for. Comes with attributes:
+ // - WIFIROUTER_ATTR_MAC - MAC address of interface that received packet,
+ // - WIFIROUTER_ATTR_PACKET - content of the MAC80211_HWSIM packet.
+ WIFIROUTER_CMD_NOTIFY,
+};
+
+// Attributes recognized by WIFIRouter netlink family.
+enum {
+ // Don't use attribute 0 to avoid parsing malformed message.
+ WIFIROUTER_ATTR_UNSPEC,
+
+ // MAC address representing interface from which the packet originated.
+ WIFIROUTER_ATTR_MAC,
+
+ // MAC80211_HWSIM packet content.
+ WIFIROUTER_ATTR_PACKET,
+
+ // Keep this last.
+ WIFIROUTER_ATTR_MAX
+};
+
+} // namespace cvd
+
diff --git a/common/libs/wifi/virtual_wifi.cc b/common/libs/wifi/virtual_wifi.cc
index 29f532f..be827bf 100644
--- a/common/libs/wifi/virtual_wifi.cc
+++ b/common/libs/wifi/virtual_wifi.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "host/commands/wifid/virtual_wifi.h"
+#include "common/libs/wifi/virtual_wifi.h"
#include <fstream>
@@ -22,10 +22,11 @@
#include <linux/nl80211.h>
#include <netlink/genl/ctrl.h>
-#include "host/commands/wifid/cmd.h"
-#include "host/commands/wifid/mac80211.h"
+#include "common/libs/wifi/cmd.h"
+#include "common/libs/wifi/mac80211.h"
+#include "common/libs/wifi/router.h"
-namespace avd {
+namespace cvd {
namespace {
// Create new HWSIM Radio.
// Returns newly created HWSIM radio number, or negative errno code.
@@ -185,6 +186,34 @@
LOG(ERROR) << "Unknown or no response from netlink.";
return -1;
}
+
+bool RegisterForRouterNotifications(Netlink* nl, uint8_t* mac_addr) {
+ Cmd msg;
+
+ if (!genlmsg_put(msg.Msg(), NL_AUTO_PID, NL_AUTO_SEQ, 0, 0,
+ NLM_F_REQUEST, WIFIROUTER_CMD_REGISTER, 0) ||
+ nla_put(msg.Msg(), WIFIROUTER_ATTR_MAC, MAX_ADDR_LEN, mac_addr)) {
+ LOG(ERROR) << "Could not create wifirouter register message.";
+ return false;
+ }
+
+ nl->WRCL().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));
+ LOG_IF(ERROR, err->error < 0) << "Failed to register with wifi router: "
+ << strerror(err->error);
+ return err->error == 0;
+ }
+ }
+
+ LOG(ERROR) << "Unknown or no response from wifi router.";
+ return -1;
+}
+
} // namespace
VirtualWIFI::~VirtualWIFI() {
@@ -198,9 +227,13 @@
}
bool VirtualWIFI::Init() {
- if (sscanf(addr_.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
+ // Dummy variable is used with sscanf to determine mac address is well formed
+ // (that is: there's no trailing string content).
+ char dummy;
+
+ if (sscanf(addr_.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx%c",
&mac_addr_[0], &mac_addr_[1], &mac_addr_[2], &mac_addr_[3],
- &mac_addr_[4], &mac_addr_[5]) != 6) {
+ &mac_addr_[4], &mac_addr_[5], &dummy) != 6) {
LOG(ERROR) << "Malformed MAC address: " << addr_;
return false;
}
@@ -213,6 +246,7 @@
// interface properties. Each radio can have more than one WLAN.
// 1. Create new MAC80211 HWSIM radio.
+ LOG(INFO) << "Creating virtual radio: " << phy;
hwsim_number_ = CreateHWSIM(nl_, phy);
if (hwsim_number_ <= 0) {
LOG(ERROR) << "Could not create HWSIM: " << strerror(-hwsim_number_);
@@ -220,6 +254,7 @@
}
// 2. Acquire the WIPHY radio number created with HWSIM radio.
+ LOG(INFO) << "Querying WIPHY number for: " << phy;
wiphy_number_ = GetWIPHYIndex(phy);
if (wiphy_number_ <= 0) {
LOG(ERROR) << "Could not create WIPHY.";
@@ -227,6 +262,7 @@
}
// 3. Query interface index.
+ LOG(INFO) << "Querying WIFI number for: " << wiphy_number_;
iface_number_ = GetWiphyInterface(nl_, wiphy_number_);
if (iface_number_ <= 0) {
LOG(ERROR) << "Could not query interface details.";
@@ -234,11 +270,20 @@
}
// 4. Apply requested interface name.
+ LOG(INFO) << "Updating interface name to: " << name_;
if (!SetWLANInterface(nl_, iface_number_, name_, &mac_addr_[0])) {
+ LOG(ERROR) << "Could not update wlan interface name.";
+ return false;
+ }
+
+ // 5. Register with wifi router.
+ LOG(INFO) << "Registering for notifications for: " << addr_;
+ if (!RegisterForRouterNotifications(nl_, mac_addr_)) {
+ LOG(ERROR) << "Could not register with wifi router.";
return false;
}
return true;
}
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/virtual_wifi.h b/common/libs/wifi/virtual_wifi.h
index 6791875..fac70e0 100644
--- a/common/libs/wifi/virtual_wifi.h
+++ b/common/libs/wifi/virtual_wifi.h
@@ -21,9 +21,9 @@
#include <netinet/in.h>
#include <linux/netdevice.h>
-#include "host/commands/wifid/netlink.h"
+#include "common/libs/wifi/netlink.h"
-namespace avd {
+namespace cvd {
// VirtualWIFI is an abstraction of an (individual) virtual WLAN device.
// A virtual WLAN is a composition of the three elements:
@@ -77,4 +77,4 @@
VirtualWIFI& operator=(const VirtualWIFI&) = delete;
};
-} // namespace avd
+} // namespace cvd
diff --git a/common/libs/wifi/virtual_wifi_manager.cc b/common/libs/wifi/virtual_wifi_manager.cc
deleted file mode 100644
index 457a907..0000000
--- a/common/libs/wifi/virtual_wifi_manager.cc
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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
diff --git a/common/libs/wifi/virtual_wifi_manager.h b/common/libs/wifi/virtual_wifi_manager.h
deleted file mode 100644
index 99d61d1..0000000
--- a/common/libs/wifi/virtual_wifi_manager.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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 <map>
-#include <memory>
-#include <mutex>
-
-#include "host/commands/wifid/netlink.h"
-#include "host/commands/wifid/virtual_wifi.h"
-
-namespace avd {
-class VirtualWIFIManager {
- public:
- VirtualWIFIManager(Netlink* nl) : nl_(nl){};
- ~VirtualWIFIManager();
-
- // Initialize VirtualWIFI Manager instance.
- bool Init();
-
- // Create new VirtualWIFI instance with the specified name.
- std::shared_ptr<VirtualWIFI> CreateRadio(const std::string& name,
- const std::string& address);
-
- private:
- // Enables asynchronous notifications from MAC80211 about recently sent wifi
- // packets.
- bool RegisterForSimulatorNotifications();
-
- // Handle asynchronous netlink frame.
- // Netlink does not differentiate between frame types so this callback will
- // receive all Generic Netlink frames that do not have a proper recipient.
- void HandleNlResponse(nl_msg* m);
-
- Netlink* const nl_;
-
- // Map VirtualWIFI's MAC address to VirtualWIFI instance.
- std::map<uint64_t, std::weak_ptr<VirtualWIFI>> radios_;
- std::mutex radios_mutex_;
-
- VirtualWIFIManager(const VirtualWIFIManager&) = delete;
- VirtualWIFIManager& operator=(const VirtualWIFIManager&) = delete;
-};
-
-} // namespace avd
diff --git a/common/libs/wifi/wr_client.cc b/common/libs/wifi/wr_client.cc
new file mode 100644
index 0000000..65fd170
--- /dev/null
+++ b/common/libs/wifi/wr_client.cc
@@ -0,0 +1,103 @@
+/*
+ * 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/wr_client.h"
+
+#include <glog/logging.h>
+
+namespace cvd {
+namespace {
+const int kMaxSupportedPacketSize = getpagesize();
+} // namespace
+WRClient::WRClient(const std::string& address) : address_(address) {}
+
+bool WRClient::Init() {
+ // Sadly, we can't use SharedFD, because we need access to raw file
+ // descriptor.
+
+ struct sockaddr_un addr {};
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path + 1, address_.c_str(), address_.size());
+ socklen_t len = offsetof(struct sockaddr_un, sun_path) + address_.size() + 1;
+ socket_ = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (socket_ < 0) {
+ LOG(ERROR) << "socket() failed: " << strerror(errno);
+ return false;
+ }
+
+ auto res = connect(socket_, reinterpret_cast<sockaddr*>(&addr), len);
+ if (res < 0) {
+ LOG(ERROR) << "Could not connect to wifi router: " << strerror(errno);
+ return false;
+ }
+
+ return true;
+}
+
+void WRClient::Send(Cmd* msg) {
+ std::lock_guard<std::mutex> guard(in_flight_mutex_);
+ // Make sure to execute this while in critical section to ensure we have time
+ // to set up seq number & callback before we receive response.
+ auto hdr = nlmsg_hdr(msg->Msg());
+ int seq = in_flight_last_seq_++;
+ // Do not use 0 for sequence numbers. 0 is reserved for async notifications.
+ if (!in_flight_last_seq_) in_flight_last_seq_ = 1;
+
+ hdr->nlmsg_seq = seq;
+ send(socket_, hdr, hdr->nlmsg_len, MSG_NOSIGNAL);
+ in_flight_[seq] = msg;
+}
+
+// Handle asynchronous messages & responses from netlink.
+void WRClient::HandleResponses() {
+ std::unique_ptr<uint8_t[]> buf(new uint8_t[kMaxSupportedPacketSize]);
+
+ auto size = recv(socket_, buf.get(), kMaxSupportedPacketSize, 0);
+ if (size <= 0) {
+ LOG(FATAL) << "No data from WIFI Router - likely dead: " << strerror(errno);
+ return;
+ }
+
+ auto hdr = reinterpret_cast<nlmsghdr*>(buf.get());
+ if (size != hdr->nlmsg_len) {
+ LOG(FATAL) << "Malformed message from WIFI Router.";
+ return;
+ }
+
+ int seq = hdr->nlmsg_seq;
+ std::unique_ptr<nl_msg, void (*)(nl_msg*)> nlmsg(
+ nlmsg_convert(hdr), [](nl_msg* m) { nlmsg_free(m); });
+
+ // Find & invoke corresponding callback, if any.
+ std::lock_guard<std::mutex> guard(in_flight_mutex_);
+ auto pos = in_flight_.find(seq);
+ if (pos != in_flight_.end()) {
+ if (pos->second->OnResponse(nlmsg.get())) {
+ // Erase command if reports it's done.
+ in_flight_.erase(seq);
+ }
+ } else if (default_handler_) {
+ default_handler_(nlmsg.get());
+ }
+}
+
+void WRClient::SetDefaultHandler(std::function<void(nl_msg*)> cb) {
+ std::lock_guard<std::mutex> guard(in_flight_mutex_);
+ default_handler_ = std::move(cb);
+}
+
+int WRClient::Sock() const { return socket_; }
+
+} // namespace cvd
diff --git a/common/libs/wifi/wr_client.h b/common/libs/wifi/wr_client.h
new file mode 100644
index 0000000..71a6438
--- /dev/null
+++ b/common/libs/wifi/wr_client.h
@@ -0,0 +1,68 @@
+/*
+ * 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 <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include <netlink/msg.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "common/libs/wifi/cmd.h"
+
+namespace cvd {
+
+class WRClient {
+ public:
+ WRClient(const std::string& socket_address);
+ ~WRClient() = default;
+
+ // Init this client: open socket to wifi router.
+ bool Init();
+
+ // Get wifirouter socket used for sending and receiving messages.
+ int Sock() const;
+
+ // Send message to wifi router.
+ void Send(Cmd* msg);
+
+ // Handle incoming responses from wifi router.
+ void HandleResponses();
+
+ // Set callback receiving all asynchronous messages and responses that do not
+ // have any proper recipient.
+ void SetDefaultHandler(std::function<void(nl_msg*)> cb);
+
+ private:
+ // Receive & dispatch netlink response.
+
+ std::string address_;
+ int socket_ = 0;
+ std::mutex in_flight_mutex_;
+ // Do not use 0 as a sequence number. 0 is reserved for asynchronous
+ // notifications.
+ int in_flight_last_seq_ = 1;
+ std::map<uint32_t, Cmd*> in_flight_;
+ std::function<void(nl_msg*)> default_handler_;
+
+ WRClient(const WRClient&) = delete;
+ WRClient& operator=(const WRClient&) = delete;
+};
+
+} // namespace cvd