Virtual WIFI, stake 5: origin detection.

Unfortunately, MAC80211HWSIM leaves MAC ADDRESS as the only method of detecting packet origin.
This is sad, because on more sophisticated systems, like ours is about to be, modification of
mac address is simple, but will kill communication. The only way to fix this would be to
extend MAC80211 packets with HWSIM_ATTR_RADIO_ID, which is there, but for some reason has
been decided to not be included in the packet.

Change-Id: I59cc7de64d0f9a34d4a9d2d9ecdbdeeda84d71da
diff --git a/common/libs/wifi/virtual_wifi.cc b/common/libs/wifi/virtual_wifi.cc
index 819472a..29f532f 100644
--- a/common/libs/wifi/virtual_wifi.cc
+++ b/common/libs/wifi/virtual_wifi.cc
@@ -153,7 +153,8 @@
 
 // Set WLAN interface name.
 // Uses Netlink Route to alter interface attributes (currently: name).
-bool SetWLANInterface(Netlink* nl, int iface_index, const std::string& name) {
+bool SetWLANInterface(Netlink* nl, int iface_index, const std::string& name,
+                      const uint8_t* address) {
   Cmd msg;
 
   ifinfomsg ifm{};
@@ -162,7 +163,8 @@
   if (!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())) {
+      nla_put_string(msg.Msg(), IFLA_IFNAME, name.c_str()) ||
+      nla_put(msg.Msg(), IFLA_ADDRESS, MAX_ADDR_LEN, address)) {
     LOG(ERROR) << "Could not create interface update.";
     return false;
   }
@@ -196,6 +198,13 @@
 }
 
 bool VirtualWIFI::Init() {
+  if (sscanf(addr_.c_str(), "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
+             &mac_addr_[0], &mac_addr_[1], &mac_addr_[2], &mac_addr_[3],
+             &mac_addr_[4], &mac_addr_[5]) != 6) {
+    LOG(ERROR) << "Malformed MAC address: " << addr_;
+    return false;
+  }
+
   std::string phy = name_ + "phy";
   // Each WLAN device consists of two sides:
   // - WIPHY is the "radio" side,
@@ -225,7 +234,7 @@
   }
 
   // 4. Apply requested interface name.
-  if (!SetWLANInterface(nl_, iface_number_, name_)) {
+  if (!SetWLANInterface(nl_, iface_number_, name_, &mac_addr_[0])) {
     return false;
   }
 
diff --git a/common/libs/wifi/virtual_wifi.h b/common/libs/wifi/virtual_wifi.h
index bf12d6d..6791875 100644
--- a/common/libs/wifi/virtual_wifi.h
+++ b/common/libs/wifi/virtual_wifi.h
@@ -18,6 +18,9 @@
 #include <memory>
 #include <string>
 
+#include <netinet/in.h>
+#include <linux/netdevice.h>
+
 #include "host/commands/wifid/netlink.h"
 
 namespace avd {
@@ -38,10 +41,12 @@
 // at any given time.
 class VirtualWIFI {
  public:
-  VirtualWIFI(Netlink* nl, const std::string& name) : nl_(nl), name_(name) {}
+  VirtualWIFI(Netlink* nl, const std::string& name, const std::string& macaddr)
+      : nl_(nl), name_(name), addr_(macaddr) {}
   ~VirtualWIFI();
 
-  int HwSimNumber() const { return hwsim_number_; }
+  const uint8_t* MacAddr() const { return &mac_addr_[0]; }
+  const std::string& Name() const { return name_; }
 
   bool Init();
 
@@ -49,7 +54,22 @@
   Netlink* nl_;
   std::string name_;
 
+  // MAC address associated with primary WLAN interface.
+  // This is the only way to identify origin of the packets.
+  // Sadly, if MAC Address is altered manually at runtime, we
+  // will stop working.
+  std::string addr_;
+
+  // NOTE: this has to be MAX_ADDR_LEN, even if we occupy fewer bytes.
+  // Netlink requires this to be full length.
+  uint8_t mac_addr_[MAX_ADDR_LEN];
+
+  // HWSIM number is required to identify HWSIM device that we want destroyed
+  // when we no longer need it.
   int hwsim_number_ = 0;
+
+  // WIPHY and WIFI interface numbers. Useful for local operations, such as
+  // renaming interface.
   int wiphy_number_ = 0;
   int iface_number_ = 0;
 
diff --git a/common/libs/wifi/virtual_wifi_manager.cc b/common/libs/wifi/virtual_wifi_manager.cc
index 3edd453..457a907 100644
--- a/common/libs/wifi/virtual_wifi_manager.cc
+++ b/common/libs/wifi/virtual_wifi_manager.cc
@@ -22,6 +22,16 @@
 #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
@@ -63,14 +73,40 @@
 }
 
 void VirtualWIFIManager::HandleNlResponse(nl_msg* m) {
-  LOG(INFO) << "Netlink response received." << 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) {
-  std::shared_ptr<VirtualWIFI> wifi(new VirtualWIFI(nl_, name));
+    const std::string& name, const std::string& address) {
+  std::shared_ptr<VirtualWIFI> wifi(new VirtualWIFI(nl_, name, address));
 
   if (!wifi->Init()) {
     wifi.reset();
@@ -78,7 +114,7 @@
   }
 
   std::lock_guard<std::mutex> lock(radios_mutex_);
-  radios_[wifi->HwSimNumber()] = wifi;
+  radios_[MACToKey(wifi->MacAddr())] = wifi;
   return wifi;
 }
 
diff --git a/common/libs/wifi/virtual_wifi_manager.h b/common/libs/wifi/virtual_wifi_manager.h
index b1f34d1..99d61d1 100644
--- a/common/libs/wifi/virtual_wifi_manager.h
+++ b/common/libs/wifi/virtual_wifi_manager.h
@@ -32,7 +32,8 @@
   bool Init();
 
   // Create new VirtualWIFI instance with the specified name.
-  std::shared_ptr<VirtualWIFI> CreateRadio(const std::string& name);
+  std::shared_ptr<VirtualWIFI> CreateRadio(const std::string& name,
+                                           const std::string& address);
 
  private:
   // Enables asynchronous notifications from MAC80211 about recently sent wifi
@@ -46,8 +47,8 @@
 
   Netlink* const nl_;
 
-  // Map VirtualWIFI's Radio ID to VirtualWIFI instance.
-  std::map<int, std::weak_ptr<VirtualWIFI>> radios_;
+  // 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;