Clone of wifirouter from partner repo

Change-Id: Ic41fde03d9acc4a03d0273a01019001d15ece7ae
Merged-In: Ic41fde03d9acc4a03d0273a01019001d15ece7ae
(cherry picked from commit a881de5c05830abfeaec7499329286b6027bf259)
diff --git a/common/libs/wifi/router.h b/common/libs/wifi/router.h
index 364de94..237417d 100644
--- a/common/libs/wifi/router.h
+++ b/common/libs/wifi/router.h
@@ -21,15 +21,22 @@
   // 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_ATTR_HWSIM_ID - ID of HWSIM card to receive notifications for,
+  // - WIFIROUTER_ATTR_HWSIM_ADDR - 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_HWSIM_ID - MAC address of interface that received packet,
   // - WIFIROUTER_ATTR_PACKET - content of the MAC80211_HWSIM packet.
   WIFIROUTER_CMD_NOTIFY,
+
+  // WIFIROUTER_RMD_SEND is issued by the client to request injection of a
+  // packet to all interfaces the client is registered for. Comes with
+  // attributes:
+  // - WIFIROUTER_ATTR_PACKET - content of the MAC80211_HWSIM packet.
+  WIFIROUTER_CMD_SEND,
 };
 
 // Attributes recognized by WIFIRouter netlink family.
@@ -40,6 +47,9 @@
   // MAC address representing interface from which the packet originated.
   WIFIROUTER_ATTR_HWSIM_ID,
 
+  // Physical address of wireless interface.
+  WIFIROUTER_ATTR_HWSIM_ADDR,
+
   // MAC80211_HWSIM packet content.
   WIFIROUTER_ATTR_PACKET,
 
diff --git a/guest/commands/wifirouter/router.cc b/guest/commands/wifirouter/router.cc
index a143ffb..47727f3 100644
--- a/guest/commands/wifirouter/router.cc
+++ b/guest/commands/wifirouter/router.cc
@@ -17,12 +17,15 @@
 
 #include <cerrno>
 #include <cstddef>
+#include <iomanip>
 #include <map>
 #include <memory>
 #include <set>
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
+#include <netinet/in.h>
+#include <linux/netdevice.h>
 #include <netlink/genl/ctrl.h>
 #include <netlink/genl/family.h>
 #include <netlink/genl/genl.h>
@@ -34,23 +37,87 @@
 DEFINE_string(socket_name, "cvd-wifirouter",
               "Name of the unix-domain socket providing access for routing. "
               "Socket will be created in abstract namespace.");
+DEFINE_bool(use_fixed_addresses, false,
+            "Specify to use hard-coded WIFI addresses issued by MAC80211 HWSIM."
+            " This is relevant for systems, where mac address update is not"
+            " reflected in mac80211_hwsim module.");
+DEFINE_bool(log_broadcast_frames, false, "Specify to log broadcast frames.");
 
 namespace cvd {
-namespace {
 // Copied out of mac80211_hwsim.h header.
 constexpr int HWSIM_CMD_REGISTER = 1;
+constexpr int HWSIM_CMD_FRAME = 2;
+constexpr int HWSIM_CMD_TX_INFO_FRAME = 3;
+
+constexpr int HWSIM_TX_CTL_REQ_TX_STATUS = 1;
+constexpr int HWSIM_TX_CTL_NO_ACK = 2;
+constexpr int HWSIM_TX_STAT_ACK = 4;
+
+constexpr int HWSIM_ATTR_ADDR_RECEIVER = 1;
 constexpr int HWSIM_ATTR_ADDR_TRANSMITTER = 2;
+constexpr int HWSIM_ATTR_FRAME = 3;
+constexpr int HWSIM_ATTR_FLAGS = 4;
+constexpr int HWSIM_ATTR_RX_RATE = 5;
+constexpr int HWSIM_ATTR_SIGNAL = 6;
+constexpr int HWSIM_ATTR_TX_INFO = 7;
+constexpr int HWSIM_ATTR_COOKIE = 8;
 constexpr int HWSIM_ATTR_MAX = 19;
 
 // Name of the WIFI SIM Netlink Family.
 constexpr char kWifiSimFamilyName[] = "MAC80211_HWSIM";
 const int kMaxSupportedPacketSize = getpagesize();
 
+constexpr int kDefaultSignalLevel = -24;
+
+using MACAddress = uint8_t[6];
+
+struct IEEE80211Hdr {
+  uint16_t frame_control;
+  uint16_t duration_id;
+  MACAddress destination;
+  MACAddress source;
+  MACAddress bssid;
+  uint16_t seq;
+
+  bool IsBroadcast() const;
+} __attribute__((packed));
+
+std::ostream& operator<<(std::ostream& out, const MACAddress& addr) {
+  out << std::hex
+      << std::setfill('0') << std::setw(2) << int(addr[0]) << ':'
+      << std::setfill('0') << std::setw(2) << int(addr[1]) << ':'
+      << std::setfill('0') << std::setw(2) << int(addr[2]) << ':'
+      << std::setfill('0') << std::setw(2) << int(addr[3]) << ':'
+      << std::setfill('0') << std::setw(2) << int(addr[4]) << ':'
+      << std::setfill('0') << std::setw(2) << int(addr[5]) << std::dec;
+
+  return out;
+}
+
+std::ostream& operator<<(std::ostream& out, const IEEE80211Hdr& frm) {
+  out << "IEEE80211Hdr{ Type=" << std::hex << std::setw(4) << std::setfill('0')
+      << frm.frame_control << std::dec
+      << " From=" << frm.source
+      << " To=" << frm.destination << " Via=" << frm.bssid << " }";
+  return out;
+}
+
+bool IEEE80211Hdr::IsBroadcast() const {
+  return (destination[0] & destination[1] & destination[2] & destination[3] &
+      destination[4] & destination[5]) == 0xff;
+}
+
 class WifiRouter {
  public:
-  using MacHash = uint16_t;
-  using MacToClientsTable = std::multimap<MacHash, int>;
-  using ClientsTable = std::set<int>;
+  using RadioID = int32_t;
+  using Radio = struct {
+    RadioID id;
+    uint8_t mac[ETH_ALEN];
+  };
+  using RadioToClientsTable = std::multimap<RadioID, int>;
+  using ClientToRadiosTable = std::multimap<int, Radio>;
+  using MacAddrToRadioIDTable = std::map<uint64_t, RadioID>;
+  const RadioID RadioID_Invalid = -1;
 
   WifiRouter() : sock_(nullptr, nl_socket_free) {}
   ~WifiRouter() = default;
@@ -59,7 +126,8 @@
   void ServerLoop();
 
  private:
-  MacHash GetMacHash(const void* macaddr);
+  void AddRadioID(int client, RadioID radio_id, const void* macaddr);
+  RadioID GetRadioID(const void* macaddr);
   void CreateWifiRouterServerSocket();
 
   void RegisterForHWSimNotifications();
@@ -72,17 +140,56 @@
   std::unique_ptr<nl_sock, void (*)(nl_sock*)> sock_;
   int server_fd_ = 0;
   int mac80211_family_ = 0;
-  ClientsTable registered_clients_;
-  MacToClientsTable registered_addresses_;
+  ClientToRadiosTable registered_clients_;
+  RadioToClientsTable registered_addresses_;
+  MacAddrToRadioIDTable known_addresses_;
 };
 
-WifiRouter::MacHash WifiRouter::GetMacHash(const void* macaddr) {
-  const uint8_t* t = reinterpret_cast<const uint8_t*>(macaddr);
+void WifiRouter::AddRadioID(int client, RadioID radio_id, const void* macaddr) {
+  const uint8_t* addr_bytes = reinterpret_cast<const uint8_t*>(macaddr);
+  uint64_t mac;
+  Radio r{radio_id, {}};
 
-  // This is guaranteed to be unique. Address here is assigned at creation time
-  // and is (well) non-mutable. This is a unique ID of the MAC80211 HWSIM
-  // interface.
-  return t[3] << 8 | t[4];
+  mac = (addr_bytes[0] << 24) | (addr_bytes[1] << 16) | (addr_bytes[2] >> 8) |
+      addr_bytes[3];
+  mac <<= 16;
+  mac |= (addr_bytes[4] << 8) | addr_bytes[5];
+
+  known_addresses_[mac] = radio_id;
+  // Add two MAC addresses registered internally by MAC80211_HWSIM.
+  mac = 0x020000000000ull;
+  mac |= (radio_id << 8);
+  known_addresses_[mac] = radio_id;
+
+  mac |= 0x400000000000ull;
+  known_addresses_[mac] = radio_id;
+
+  if (FLAGS_use_fixed_addresses) {
+    r.mac[0] = mac >> 40;
+    r.mac[1] = mac >> 32;
+    r.mac[2] = mac >> 24;
+    r.mac[3] = mac >> 16;
+    r.mac[4] = mac >> 8;
+    r.mac[5] = mac;
+  } else {
+    memcpy(r.mac, macaddr, ETH_ALEN);
+  }
+  registered_addresses_.emplace(radio_id, client);
+  registered_clients_.emplace(client, r);
+}
+
+WifiRouter::RadioID WifiRouter::GetRadioID(const void* macaddr) {
+  const uint8_t* addr_bytes = reinterpret_cast<const uint8_t*>(macaddr);
+  uint64_t mac;
+
+  mac = (addr_bytes[0] << 24) | (addr_bytes[1] << 16) | (addr_bytes[2] >> 8) |
+      addr_bytes[3];
+  mac <<= 16;
+  mac |= (addr_bytes[4] << 8) | addr_bytes[5];
+
+  auto iter = known_addresses_.find(mac);
+  if (iter == known_addresses_.end()) return RadioID_Invalid;
+  return iter->second;
 }
 
 void WifiRouter::Init() {
@@ -98,6 +205,11 @@
 void WifiRouter::RegisterForHWSimNotifications() {
   sock_.reset(nl_socket_alloc());
 
+  // Disable sequence number checks. Occasional "Message sequence number
+  // mismatch" errors were observed, despite netlink allocating sequence numbers
+  // itself.
+  nl_socket_disable_seq_check(sock_.get());
+
   auto res = nl_connect(sock_.get(), NETLINK_GENERIC);
   if (res < 0) {
     LOG(ERROR) << "Could not connect to netlink generic: " << nl_geterror(res);
@@ -158,7 +270,7 @@
     return;
   }
 
-  registered_clients_.insert(client);
+  registered_clients_.insert({client, {RadioID_Invalid, {}}});
   LOG(INFO) << "Client " << client << " added.";
 }
 
@@ -166,6 +278,7 @@
 // of WLAN traffic.
 void WifiRouter::RemoveClient(int client) {
   close(client);
+
   registered_clients_.erase(client);
 
   for (auto iter = registered_addresses_.begin();
@@ -197,6 +310,12 @@
   // Discard messages that originate from anything else than MAC80211_HWSIM.
   if (msg->nlmsg_type != mac80211_family_) return;
 
+  genlmsghdr* gmsg = reinterpret_cast<genlmsghdr*>(nlmsg_data(msg.get()));
+  if (gmsg->cmd != HWSIM_CMD_FRAME) {
+    LOG(INFO) << "Discarding non-FRAME message.";
+    return;
+  }
+
   std::unique_ptr<nl_msg, void (*)(nl_msg*)> rep(
       nlmsg_alloc(), [](nl_msg* m) { nlmsg_free(m); });
   genlmsg_put(rep.get(), 0, 0, 0, 0, 0, WIFIROUTER_CMD_NOTIFY, 0);
@@ -206,15 +325,20 @@
   nlattr* attrs[HWSIM_ATTR_MAX + 1];
   if (genlmsg_parse(msg.get(), 0, attrs, HWSIM_ATTR_MAX, nullptr)) return;
 
+  auto ieee80211hdr = reinterpret_cast<IEEE80211Hdr*>(nla_data(attrs[HWSIM_ATTR_FRAME]));
+  if (!ieee80211hdr->IsBroadcast() || FLAGS_log_broadcast_frames) {
+    LOG(INFO) << "SND " << *ieee80211hdr;
+  }
+
   std::set<int> pending_removals;
   auto addr = attrs[HWSIM_ATTR_ADDR_TRANSMITTER];
   if (addr != nullptr) {
-    nla_put_u32(rep.get(), WIFIROUTER_ATTR_HWSIM_ID, GetMacHash(nla_data(addr)));
+    nla_put_u32(rep.get(), WIFIROUTER_ATTR_HWSIM_ID,
+                GetRadioID(nla_data(addr)));
     nla_put(rep.get(), WIFIROUTER_ATTR_PACKET, len, buf);
     auto hdr = nlmsg_hdr(rep.get());
 
-    auto key = GetMacHash(nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]));
-    LOG(INFO) << "Received netlink packet from " << std::hex << key;
+    auto key = GetRadioID(nla_data(attrs[HWSIM_ATTR_ADDR_TRANSMITTER]));
     for (auto it = registered_addresses_.find(key);
          it != registered_addresses_.end() && it->first == key; ++it) {
       auto num_written = send(it->second, hdr, hdr->nlmsg_len, MSG_NOSIGNAL);
@@ -241,23 +365,72 @@
   int result = -EINVAL;
   genlmsghdr* ghdr = reinterpret_cast<genlmsghdr*>(nlmsg_data(msg.get()));
 
+  nlattr* attrs[WIFIROUTER_ATTR_MAX];
+  if (nlmsg_parse(msg.get(), sizeof(genlmsghdr), attrs, WIFIROUTER_ATTR_MAX - 1,
+                  nullptr))
+    return false;
+
   switch (ghdr->cmd) {
     case WIFIROUTER_CMD_REGISTER:
-      // Register client to receive notifications for specified MAC address.
-      nlattr* attrs[WIFIROUTER_ATTR_MAX];
-      if (!nlmsg_parse(msg.get(), sizeof(genlmsghdr), attrs,
-                       WIFIROUTER_ATTR_MAX - 1, nullptr)) {
-        if (attrs[WIFIROUTER_ATTR_HWSIM_ID] != nullptr) {
-          LOG(INFO) << "Registering new client to receive data for "
-                    << nla_get_u32(attrs[WIFIROUTER_ATTR_HWSIM_ID]);
-          registered_addresses_.emplace(
-              nla_get_u32(attrs[WIFIROUTER_ATTR_HWSIM_ID]), client);
-          // This is unfortunate, but it is a bug in mac80211_hwsim stack.
-          // Apparently, the imperfect medium will not receive notifications for
-          // newly created wifi interfaces. How about that...
-          RegisterForHWSimNotifications();
-          result = 0;
+      if (attrs[WIFIROUTER_ATTR_HWSIM_ID] != nullptr) {
+        int simid = nla_get_u32(attrs[WIFIROUTER_ATTR_HWSIM_ID]);
+        uint8_t* simaddr = reinterpret_cast<uint8_t*>(
+            nla_data(attrs[WIFIROUTER_ATTR_HWSIM_ADDR]));
+
+        AddRadioID(client, simid, simaddr);
+        // This is unfortunate, but it is a bug in mac80211_hwsim stack.
+        // Apparently, the imperfect medium will not receive notifications for
+        // newly created wifi interfaces. How about that...
+        RegisterForHWSimNotifications();
+        result = 0;
+      }
+      break;
+
+    case WIFIROUTER_CMD_SEND:
+      if (attrs[WIFIROUTER_ATTR_PACKET] != nullptr) {
+        std::unique_ptr<nl_msg, void (*)(nl_msg*)> frame(
+            nlmsg_convert(reinterpret_cast<nlmsghdr*>(
+                nla_data(attrs[WIFIROUTER_ATTR_PACKET]))),
+            nlmsg_free);
+
+        // Netlink is not smart enough to re-alloc.
+        nlmsg_expand(frame.get(), nlmsg_get_max_size(frame.get()) + 64);
+
+        auto hdr = nlmsg_hdr(frame.get());
+        hdr->nlmsg_type = mac80211_family_;
+        hdr->nlmsg_flags = NLM_F_REQUEST;
+
+        auto pktdata = nlmsg_find_attr(nlmsg_hdr(frame.get()),
+                                     sizeof(genlmsghdr),
+                                     HWSIM_ATTR_FRAME);
+        auto ieee80211hdr = reinterpret_cast<IEEE80211Hdr*>(nla_data(pktdata));
+        if (!ieee80211hdr->IsBroadcast() || FLAGS_log_broadcast_frames) {
+          LOG(INFO) << "RCV " << *ieee80211hdr;
         }
+
+        auto receiver =
+            nla_reserve(frame.get(), HWSIM_ATTR_ADDR_RECEIVER, ETH_ALEN);
+        if (nla_put_u32(frame.get(), HWSIM_ATTR_RX_RATE, 1) ||
+            nla_put_u32(frame.get(), HWSIM_ATTR_SIGNAL, kDefaultSignalLevel) ||
+            !receiver) {
+          LOG(ERROR) << "Could not add netlink attribute: buffer too short.";
+        } else {
+          uint8_t* macaddr = reinterpret_cast<uint8_t*>(nla_data(receiver));
+          for (auto iter = registered_clients_.find(client);
+               iter->first == client; ++iter) {
+            if (iter->second.id == RadioID_Invalid) continue;
+            memcpy(macaddr, iter->second.mac, ETH_ALEN);
+            hdr->nlmsg_seq = NL_AUTO_SEQ;
+            hdr->nlmsg_pid = NL_AUTO_PID;
+            nl_send_auto(sock_.get(), frame.get());
+            auto res = nl_wait_for_ack(sock_.get());
+            if (res) {
+              LOG(INFO) << "Packet send from " << client << " to "
+                        << iter->second.id << " result: " << nl_geterror(-res);
+            }
+          }
+        }
+        result = 0;
       }
       break;
 
@@ -290,27 +463,29 @@
 
     fdset(server_fd_);
     fdset(nl_socket_get_fd(sock_.get()));
-    for (int client : registered_clients_) fdset(client);
+    for (const auto& client : registered_clients_) fdset(client.first);
 
     if (select(max_fd + 1, &reads, nullptr, nullptr, nullptr) <= 0) continue;
 
     if (FD_ISSET(server_fd_, &reads)) AcceptNewClient();
     if (FD_ISSET(nl_socket_get_fd(sock_.get()), &reads)) RouteWIFIPacket();
 
+    std::set<int> rogue_clients;
     // Process any client messages left. Drop any client that is no longer
     // talking with us.
-    for (auto client = registered_clients_.begin();
-         client != registered_clients_.end();) {
-      auto cfd = *client++;
+    for (auto cfd : registered_clients_) {
       // Is our client sending us data?
-      if (FD_ISSET(cfd, &reads)) {
-        if (!HandleClientMessage(cfd)) RemoveClient(cfd);
+      if (FD_ISSET(cfd.first, &reads)) {
+        if (!HandleClientMessage(cfd.first)) rogue_clients.insert(cfd.first);
+        // Note: we iterate over multimap.
+        FD_CLR(cfd.first, &reads);
       }
     }
+
+    for (auto client : rogue_clients) RemoveClient(client);
   }
 }
 
-}  // namespace
 }  // namespace cvd
 
 int main(int argc, char* argv[]) {