Add Ethernet support to cuttlefish

Add a launcher flag (e.g. "launch_cvd -ethernet=true") which adds
another Ethernet interface to the virtual device. Ethernet devices are
already "buried" for use by wireless, but this new interface is not
wrapped and can be managed by netd. This functionality is useful for
Auto targets.

This mildly refactors some code in allocd to create separate Ethernet
and Wifi bridges and tap devices.

The code is off by default because it requires a new cuttlefish-common
package (0.9.17) and we haven't made the necessary changes to the
networkAttributes to allow Ethernet and Wifi to co-exist, so enabling
Ethernet currently breaks Wifi on phone configurations.

The created ethernet interface will be eth2 for now; once we can enable
this by default, we can move Ethernet back to eth0.

Bug: 172286896
Change-Id: I38ebf259f8eac101d867279f40cc78088a60921d
diff --git a/host/libs/allocd/alloc_utils.cpp b/host/libs/allocd/alloc_utils.cpp
index 883b42c..8c91686 100644
--- a/host/libs/allocd/alloc_utils.cpp
+++ b/host/libs/allocd/alloc_utils.cpp
@@ -63,17 +63,13 @@
   return status == 0;
 }
 
-bool CreateWirelessIface(const std::string& name, bool has_ipv4_bridge,
-                         bool has_ipv6_bridge, bool use_ebtables_legacy) {
+bool CreateEthernetIface(const std::string& name, const std::string& bridge_name,
+                         bool has_ipv4_bridge, bool has_ipv6_bridge,
+                         bool use_ebtables_legacy) {
   // assume bridge exists
 
-  WirelessNetworkConfig config{false, false, false};
+  EthernetNetworkConfig config{false, false, false};
 
-  // TODO (paulkirth): change this to cvd-wbr, to test w/ today's debian
-  // package, this is required since the number of wireless bridges provided by
-  // the debian package has gone from 10 down to 1, but our debian packages in
-  // cloudtop are not up to date
-  auto bridge_name = "cvd-wbr-01";
   if (!CreateTap(name)) {
     return false;
   }
@@ -81,13 +77,13 @@
   config.has_tap = true;
 
   if (!LinkTapToBridge(name, bridge_name)) {
-    CleanupWirelessIface(name, config);
+    CleanupEthernetIface(name, config);
     return false;
   }
 
   if (!has_ipv4_bridge) {
     if (!CreateEbtables(name, true, use_ebtables_legacy)) {
-      CleanupWirelessIface(name, config);
+      CleanupEthernetIface(name, config);
       return false;
     }
     config.has_broute_ipv4 = true;
@@ -95,7 +91,7 @@
 
   if (!has_ipv6_bridge) {
     if (CreateEbtables(name, false, use_ebtables_legacy)) {
-      CleanupWirelessIface(name, config);
+      CleanupEthernetIface(name, config);
       return false;
     }
     config.has_broute_ipv6 = true;
@@ -183,7 +179,7 @@
   return status == 0;
 }
 
-bool DestroyWirelessIface(const std::string& name, bool has_ipv4_bridge,
+bool DestroyEthernetIface(const std::string& name, bool has_ipv4_bridge,
                           bool has_ipv6_bridge, bool use_ebtables_legacy) {
   if (!has_ipv6_bridge) {
     DestroyEbtables(name, false, use_ebtables_legacy);
@@ -196,8 +192,8 @@
   return DestroyIface(name);
 }
 
-void CleanupWirelessIface(const std::string& name,
-                          const WirelessNetworkConfig& config) {
+void CleanupEthernetIface(const std::string& name,
+                          const EthernetNetworkConfig& config) {
   if (config.has_broute_ipv6) {
     DestroyEbtables(name, false, config.use_ebtables_legacy);
   }
@@ -458,12 +454,13 @@
   return status == 0;
 }
 
-bool CreateWirelessBridgeIface(const std::string& name) {
+bool CreateEthernetBridgeIface(const std::string& name,
+                               const std::string& ipaddr) {
   if (!CreateBridge(name)) {
     return false;
   }
 
-  if (!SetupBridgeGateway(name, kWirelessIp)) {
+  if (!SetupBridgeGateway(name, ipaddr)) {
     DestroyBridge(name);
     return false;
   }
@@ -471,12 +468,13 @@
   return true;
 }
 
-bool DestroyWirelessBridgeIface(const std::string& name) {
+bool DestroyEthernetBridgeIface(const std::string& name,
+                                const std::string& ipaddr) {
   GatewayConfig config{true, true, true};
 
   // Don't need to check if removing some part of the config failed, we need to
   // remove the entire interface, so just ignore any error until the end
-  CleanupBridgeGateway(name, kWirelessIp, config);
+  CleanupBridgeGateway(name, ipaddr, config);
 
   return DestroyBridge(name);
 }
diff --git a/host/libs/allocd/alloc_utils.h b/host/libs/allocd/alloc_utils.h
index 5c1fd76..63f6d26 100644
--- a/host/libs/allocd/alloc_utils.h
+++ b/host/libs/allocd/alloc_utils.h
@@ -36,6 +36,8 @@
 constexpr char kWirelessIp[] = "192.168.96";
 // Mobile network prefix
 constexpr char kMobileIp[] = "192.168.97";
+// Ethernet network prefix
+constexpr char kEthernetIp[] = "192.168.98";
 // permission bits for socket
 constexpr int kSocketMode = 0666;
 
@@ -46,7 +48,7 @@
 constexpr uint32_t kMaxIfaceNameId = 63;
 
 // struct for managing configuration state
-struct WirelessNetworkConfig {
+struct EthernetNetworkConfig {
   bool has_broute_ipv4 = false;
   bool has_broute_ipv6 = false;
   bool has_tap = false;
@@ -89,12 +91,14 @@
 bool DestroyMobileIface(const std::string& name, uint16_t id,
                         const std::string& ipaddr);
 
-bool CreateWirelessIface(const std::string& name, bool has_ipv4_bridge,
-                         bool has_ipv6_bridge, bool use_ebtables_legacy);
-bool DestroyWirelessIface(const std::string& name, bool has_ipv4_bridge,
-                          bool use_ipv6, bool use_ebtables_legacy);
-void CleanupWirelessIface(const std::string& name,
-                          const WirelessNetworkConfig& config);
+bool CreateEthernetIface(const std::string& name, const std::string& bridge_name,
+                         bool has_ipv4_bridge, bool has_ipv6_bridge,
+                         bool use_ebtables_legacy);
+bool DestroyEthernetIface(const std::string& name,
+                          bool has_ipv4_bridge, bool use_ipv6,
+                          bool use_ebtables_legacy);
+void CleanupEthernetIface(const std::string& name,
+                          const EthernetNetworkConfig& config);
 
 bool IptableConfig(const std::string& network, bool add);
 
@@ -105,8 +109,10 @@
 void CleanupBridgeGateway(const std::string& name, const std::string& ipaddr,
                           const GatewayConfig& config);
 
-bool CreateWirelessBridgeIface(const std::string& name);
-bool DestroyWirelessBridgeIface(const std::string& name);
+bool CreateEthernetBridgeIface(const std::string& name,
+                               const std::string &ipaddr);
+bool DestroyEthernetBridgeIface(const std::string& name,
+                                const std::string &ipaddr);
 
 bool AddGateway(const std::string& name, const std::string& gateway,
                 const std::string& netmask);
diff --git a/host/libs/allocd/request.h b/host/libs/allocd/request.h
index 8e1302d..1fbf6c9 100644
--- a/host/libs/allocd/request.h
+++ b/host/libs/allocd/request.h
@@ -45,7 +45,9 @@
   Invalid = 0,  // an invalid interface
   mtap,         // mobile tap
   wtap,         // wireless tap
-  wbr           // wireless bridge
+  etap,         // ethernet tap
+  wbr,          // wireless bridge
+  ebr           // ethernet bridge
 };
 
 enum class RequestStatus : uint16_t {
diff --git a/host/libs/allocd/resource.cpp b/host/libs/allocd/resource.cpp
index 5a8b475..881dd85 100644
--- a/host/libs/allocd/resource.cpp
+++ b/host/libs/allocd/resource.cpp
@@ -30,13 +30,13 @@
   return DestroyMobileIface(GetName(), iface_id_, ipaddr_);
 }
 
-bool WirelessIface::AcquireResource() {
-  return CreateWirelessIface(GetName(), has_ipv4_, has_ipv6_,
+bool EthernetIface::AcquireResource() {
+  return CreateEthernetIface(GetName(), GetBridgeName(), has_ipv4_, has_ipv6_,
                              use_ebtables_legacy_);
 }
 
-bool WirelessIface::ReleaseResource() {
-  return DestroyWirelessIface(GetName(), has_ipv4_, has_ipv6_,
+bool EthernetIface::ReleaseResource() {
+  return DestroyEthernetIface(GetName(), has_ipv4_, has_ipv6_,
                               use_ebtables_legacy_);
 }
 
diff --git a/host/libs/allocd/resource.h b/host/libs/allocd/resource.h
index a92d38b..c30e9cc 100644
--- a/host/libs/allocd/resource.h
+++ b/host/libs/allocd/resource.h
@@ -26,8 +26,8 @@
 enum class ResourceType {
   Invalid = 0,
   MobileIface,
-  WirelessIface,
-  WirelessBridge
+  EthernetIface,
+  EthernetBridge,
 };
 
 class StaticResource {
@@ -75,21 +75,25 @@
   std::string ipaddr_;
 };
 
-class WirelessIface : public StaticResource {
+class EthernetIface : public StaticResource {
  public:
-  WirelessIface() = default;
-  ~WirelessIface() = default;
+  EthernetIface() = default;
+  ~EthernetIface() = default;
 
-  WirelessIface(const std::string& name, uid_t uid, uint16_t iface_id,
-                uint32_t global_id, std::string ipaddr)
+  EthernetIface(const std::string& name, uid_t uid, uint16_t iface_id,
+                uint32_t global_id, std::string bridge_name,
+                std::string ipaddr)
       : StaticResource(name, uid, ResourceType::MobileIface, global_id),
         iface_id_(iface_id),
+        bridge_name_(bridge_name),
         ipaddr_(ipaddr) {}
 
   bool ReleaseResource() override;
   bool AcquireResource() override;
 
   uint16_t GetIfaceId() { return iface_id_; }
+
+  std::string GetBridgeName() { return bridge_name_; }
   std::string GetIpAddr() { return ipaddr_; }
 
   void SetHasIpv4(bool ipv4) { has_ipv4_ = ipv4; }
@@ -105,6 +109,7 @@
  private:
   static constexpr char kNetmask[] = "/24";
   uint16_t iface_id_;
+  std::string bridge_name_;
   std::string ipaddr_;
   bool has_ipv4_ = true;
   bool has_ipv6_ = true;
diff --git a/host/libs/allocd/resource_manager.cpp b/host/libs/allocd/resource_manager.cpp
index 1ec6ac1..aaa8fe0 100644
--- a/host/libs/allocd/resource_manager.cpp
+++ b/host/libs/allocd/resource_manager.cpp
@@ -85,16 +85,20 @@
     const char* idp = iface.c_str() + (iface.size() - 3);
     int small_id = atoi(idp);
     switch (ty) {
-      case IfaceType::mtap: {
+      case IfaceType::mtap:
         res = std::make_shared<MobileIface>(iface, uid, small_id, resource_id,
                                             kMobileIp);
         allocatedIface = res->AcquireResource();
         pending_add_.insert({resource_id, res});
         break;
-      }
       case IfaceType::wtap: {
-        auto w = std::make_shared<WirelessIface>(iface, uid, small_id,
-                                                 resource_id, kMobileIp);
+        // TODO (paulkirth): change this to cvd-wbr, to test w/ today's
+        // debian package, this is required since the number of wireless
+        // bridges provided by the debian package has gone from 10 down to
+        // 1, but our debian packages in cloudtop are not up to date
+        auto w = std::make_shared<EthernetIface>(iface, uid, small_id,
+                                                 resource_id, "cvd-wbr-01",
+                                                 kWirelessIp);
         w->SetUseEbtablesLegacy(use_ebtables_legacy_);
         w->SetHasIpv4(use_ipv4_bridge_);
         w->SetHasIpv6(use_ipv6_bridge_);
@@ -103,10 +107,22 @@
         pending_add_.insert({resource_id, res});
         break;
       }
-      case IfaceType::wbr: {
-        allocatedIface = CreateBridge(iface);
+      case IfaceType::etap: {
+        auto w = std::make_shared<EthernetIface>(iface, uid, small_id,
+                                                 resource_id, "cvd-ebr",
+                                                 kEthernetIp);
+        w->SetUseEbtablesLegacy(use_ebtables_legacy_);
+        w->SetHasIpv4(use_ipv4_bridge_);
+        w->SetHasIpv6(use_ipv6_bridge_);
+        res = w;
+        allocatedIface = res->AcquireResource();
+        pending_add_.insert({resource_id, res});
         break;
       }
+      case IfaceType::wbr:
+      case IfaceType::ebr:
+        allocatedIface = CreateBridge(iface);
+        break;
       case IfaceType::Invalid:
         break;
     }
@@ -138,15 +154,15 @@
         removedIface = DestroyMobileIface(iface, id, kMobileIp);
         break;
       }
-      case IfaceType::wtap: {
-        removedIface = DestroyWirelessIface(
+      case IfaceType::wtap:
+      case IfaceType::etap:
+        removedIface = DestroyEthernetIface(
             iface, use_ipv4_bridge_, use_ipv6_bridge_, use_ebtables_legacy_);
         break;
-      }
-      case IfaceType::wbr: {
+      case IfaceType::wbr:
+      case IfaceType::ebr:
         removedIface = DestroyBridge(iface);
         break;
-      }
       case IfaceType::Invalid:
         break;
     }
diff --git a/host/libs/allocd/utils.cpp b/host/libs/allocd/utils.cpp
index 603c8d0..c60dc0c 100644
--- a/host/libs/allocd/utils.cpp
+++ b/host/libs/allocd/utils.cpp
@@ -58,13 +58,17 @@
     {"invalid", IfaceType::Invalid},
     {"mtap", IfaceType::mtap},
     {"wtap", IfaceType::wtap},
-    {"wbr", IfaceType::wbr}};
+    {"etap", IfaceType::etap},
+    {"wbr", IfaceType::wbr},
+    {"ebr", IfaceType::ebr}};
 
 const std::map<IfaceType, std::string> IfaceTyToStrMap = {
     {IfaceType::Invalid, "invalid"},
     {IfaceType::mtap, "mtap"},
     {IfaceType::wtap, "wtap"},
-    {IfaceType::wbr, "wbr"}};
+    {IfaceType::etap, "etap"},
+    {IfaceType::wbr, "wbr"},
+    {IfaceType::ebr, "ebr"}};
 
 const std::map<RequestStatus, std::string> ReqStatusToStrMap = {
     {RequestStatus::Invalid, "invalid"},
@@ -167,8 +171,12 @@
       return "mtap";
     case IfaceType::wtap:
       return "wtap";
+    case IfaceType::etap:
+      return "etap";
     case IfaceType::wbr:
       return "wbr";
+    case IfaceType::ebr:
+      return "ebr";
   }
 }