Create a guest VLAN network with a host flag.

From the host:
 - A new flag is added to switch from guest vlan/no-vlan.
 - no-vlan runs the same network configuration as before.
 - vlan exposes just the one tap device to qemu.
 - The default is to not use it, but in the future the host could use
   capability_query to decide what to set as the default.

From the guest:
 - ip_link_add is replaced with a single setup_network binary
 - setup_network uses a flag as a signal for vlan/no-vlan
 - no-vlan has the same behavior as the previous network configuration
 - vlan configures the mobile and wifi vlans
 - using a network namespace is possible future work

Bug: 111320496
Test: Ran cf_x86_phone-userdebug with and without vlans on a host.
Change-Id: I47cf4e1b1c70a339218d24053f29ffbcf9887263
Merged-In: I47cf4e1b1c70a339218d24053f29ffbcf9887263
diff --git a/guest/commands/ip_link_add/main.cpp b/guest/commands/ip_link_add/main.cpp
deleted file mode 100644
index 5b07798..0000000
--- a/guest/commands/ip_link_add/main.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2018 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/net/netlink_client.h"
-#include "common/libs/net/netlink_request.h"
-#include "common/libs/net/network_interface.h"
-#include "common/libs/net/network_interface_manager.h"
-
-#include <linux/rtnetlink.h>
-#include <net/if.h>
-#include <iostream>
-#include <string>
-
-int main(int argc, char *argv[]) {
-  if (!((argc == 5 && std::string(argv[1]) == "vlan") ||
-        (argc == 4 && std::string(argv[1]) == "virt_wifi"))) {
-    std::cerr << "usages:\n";
-    std::cerr << "  " << argv[0] << " vlan [ethA] [ethB] [index]\n";
-    std::cerr << "  " << argv[0] << " virt_wifi [ethA] [ethB]\n";
-    return -1;
-  }
-  const char *const name = argv[2];
-  int32_t index = if_nametoindex(name);
-  if (index == 0) {
-    fprintf(stderr, "%s: invalid interface name '%s'\n", argv[2], name);
-    return -2;
-  }
-  const char *const new_name = argv[3];
-  auto factory = cvd::NetlinkClientFactory::Default();
-  std::unique_ptr<cvd::NetlinkClient> nl(factory->New(NETLINK_ROUTE));
-
-  // http://maz-programmersdiary.blogspot.com/2011/09/netlink-sockets.html
-  cvd::NetlinkRequest link_add_request(RTM_NEWLINK, NLM_F_REQUEST|NLM_F_ACK|0x600);
-  link_add_request.Append(ifinfomsg {
-    .ifi_change = 0xFFFFFFFF,
-  });
-  link_add_request.AddString(IFLA_IFNAME, std::string(new_name));
-  link_add_request.AddInt(IFLA_LINK, index);
-
-  link_add_request.PushList(IFLA_LINKINFO);
-  link_add_request.AddString(IFLA_INFO_KIND, argv[1]);
-  link_add_request.PushList(IFLA_INFO_DATA);
-  if (std::string(argv[1]) == "vlan") {
-    uint16_t vlan_index = atoi(argv[4]);
-    link_add_request.AddInt(IFLA_VLAN_ID, vlan_index);
-  }
-  link_add_request.PopList();
-  link_add_request.PopList();
-
-  nl->Send(link_add_request);
-
-  cvd::NetlinkRequest bring_up_backing_request(RTM_SETLINK, NLM_F_REQUEST|NLM_F_ACK|0x600);
-  bring_up_backing_request.Append(ifinfomsg {
-    .ifi_index = index,
-    .ifi_flags = IFF_UP,
-    .ifi_change = 0xFFFFFFFF,
-  });
-
-  nl->Send(bring_up_backing_request);
-
-  return 0;
-}
diff --git a/guest/commands/ip_link_add/Android.mk b/guest/commands/setup_network/Android.mk
similarity index 94%
rename from guest/commands/ip_link_add/Android.mk
rename to guest/commands/setup_network/Android.mk
index 993dea6..041c12f 100644
--- a/guest/commands/ip_link_add/Android.mk
+++ b/guest/commands/setup_network/Android.mk
@@ -16,10 +16,10 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := ip_link_add
+LOCAL_MODULE := setup_network
 LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := main.cpp
-LOCAL_SHARED_LIBRARIES := cuttlefish_net cuttlefish_auto_resources
+LOCAL_SHARED_LIBRARIES := cuttlefish_net cuttlefish_auto_resources libbase liblog
 LOCAL_C_INCLUDES := device/google/cuttlefish_common
 LOCAL_MULTILIB := first
 LOCAL_VENDOR_MODULE := true
diff --git a/guest/commands/setup_network/main.cpp b/guest/commands/setup_network/main.cpp
new file mode 100644
index 0000000..269db8c
--- /dev/null
+++ b/guest/commands/setup_network/main.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2018 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/net/netlink_client.h"
+#include "common/libs/net/netlink_request.h"
+#include "common/libs/net/network_interface.h"
+#include "common/libs/net/network_interface_manager.h"
+#include "common/libs/glog/logging.h"
+
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <iostream>
+#include <string>
+#include <cstdlib>
+
+int IpLinkAdd(const std::string& source, const std::string& destination,
+                 const std::string& type, int vlan_index) {
+  auto factory = cvd::NetlinkClientFactory::Default();
+  std::unique_ptr<cvd::NetlinkClient> nl(factory->New(NETLINK_ROUTE));
+
+  // http://maz-programmersdiary.blogspot.com/2011/09/netlink-sockets.html
+  cvd::NetlinkRequest link_add_request(RTM_NEWLINK,
+                                       NLM_F_REQUEST|NLM_F_ACK|0x600);
+  link_add_request.Append(ifinfomsg {
+    .ifi_change = 0xFFFFFFFF,
+  });
+  int32_t index = if_nametoindex(source.c_str());
+  if (index == 0) {
+    LOG(ERROR) << "setup_network: invalid interface name '" << source << "'\n";
+    return -2;
+  }
+  link_add_request.AddString(IFLA_IFNAME, destination);
+  link_add_request.AddInt(IFLA_LINK, index);
+
+  link_add_request.PushList(IFLA_LINKINFO);
+  link_add_request.AddString(IFLA_INFO_KIND, type.c_str());
+  link_add_request.PushList(IFLA_INFO_DATA);
+  if (type == "vlan") {
+    link_add_request.AddInt(IFLA_VLAN_ID, vlan_index);
+  }
+  link_add_request.PopList();
+  link_add_request.PopList();
+
+  bool link_add_success = nl->Send(link_add_request);
+  if (!link_add_success) {
+    LOG(ERROR) << "setup_network: could not add link " << destination;
+    return -3;
+  }
+
+  cvd::NetlinkRequest bring_up_backing_request(RTM_SETLINK,
+                                               NLM_F_REQUEST|NLM_F_ACK|0x600);
+  bring_up_backing_request.Append(ifinfomsg {
+    .ifi_index = index,
+    .ifi_flags = IFF_UP,
+    .ifi_change = 0xFFFFFFFF,
+  });
+
+  bool link_backing_up = nl->Send(bring_up_backing_request);
+  if (!link_backing_up) {
+    LOG(ERROR) << "setup_network: could not bring up backing " << source;
+    return -4;
+  }
+
+  return 0;
+}
+
+int CreateVlan(const std::string& source, const std::string& destination,
+               int vlan_index) {
+  return IpLinkAdd(source, destination, "vlan", vlan_index);
+}
+
+int CreateWifiWrapper(const std::string& source,
+                      const std::string& destination) {
+  return IpLinkAdd(source, destination, "virt_wifi", -1);
+}
+
+int RenameNetwork(const std::string& name, const std::string& new_name) {
+  static auto net_manager =
+      cvd::NetworkInterfaceManager::New(cvd::NetlinkClientFactory::Default());
+  auto connection = net_manager->Open(name, "ignore");
+  if (!connection) {
+    LOG(ERROR) << "setup_network: could not open " << name << " on device.";
+    return -1;
+  }
+  connection->SetName(new_name);
+  bool changes_applied = net_manager->ApplyChanges(*connection);
+  if (!changes_applied) {
+    LOG(ERROR) << "setup_network: can't rename " << name << " to " << new_name;
+    return -1;
+  }
+  return 0;
+}
+
+int main(int argc, char** argv) {
+  if (argc != 2) {
+    LOG(ERROR) << "setup_network: must be invoked with only "
+               << "${ro.boot.cuttlefish_network}";
+  }
+  int ret = 0;
+  if (std::string(argv[1]) == "vlan") {
+    ret += RenameNetwork("eth0", "buried_eth0");
+    ret += CreateVlan("buried_eth0", "buried_wlan0", 11);
+    ret += CreateWifiWrapper("buried_wlan0", "wlan0");
+    ret += CreateVlan("buried_eth0", "rmnet0", 12);
+  } else if (std::string(argv[1]) == "legacy") {
+    ret += RenameNetwork("eth0", "buried_eth0");
+    ret += CreateWifiWrapper("buried_eth0", "wlan0");
+    ret += RenameNetwork("eth1", "rmnet0");
+  } else {
+    LOG(ERROR) << "setup_network: unknown configuration '" << argv[1] << "'";
+  }
+  return ret;
+}
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 0cf4f28..39461d6 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -170,6 +170,10 @@
               "Network interface to use for wifi");
 DEFINE_string(wifi_tap_name, "", // default handled on ParseCommandLine
               "The name of the tap interface to use for wifi");
+DEFINE_string(vlan_tap_name, "", // default handled on ParseCommandLine
+              "The name of the tap interface to use for the vlan network");
+DEFINE_bool(guest_vlans, false,
+            "Force the guest to use or not use VLANs.");
 // TODO(b/72969289) This should be generated
 DEFINE_string(dtb, "", "Path to the cuttlefish.dtb file");
 
@@ -573,6 +577,8 @@
   if (FLAGS_extra_kernel_cmdline.size()) {
     config->add_kernel_cmdline(FLAGS_extra_kernel_cmdline);
   }
+  config->add_kernel_cmdline(concat("androidboot.cuttlefish_network=",
+                                 FLAGS_guest_vlans ? "vlan" : "legacy"));
 
   config->set_ramdisk_image_path(ramdisk_path);
   config->set_system_image_path(FLAGS_system_image);
@@ -608,6 +614,9 @@
   config->set_wifi_bridge_name(FLAGS_wifi_interface);
   config->set_wifi_tap_name(FLAGS_wifi_tap_name);
 
+  config->set_guest_vlans(FLAGS_guest_vlans);
+  config->set_vlan_tap_name(FLAGS_vlan_tap_name);
+
   config->set_wifi_guest_mac_addr(FLAGS_guest_mac_address);
   config->set_wifi_host_mac_addr(FLAGS_host_mac_address);
 
@@ -644,6 +653,10 @@
   SetCommandLineOptionWithMode("wifi_tap_name",
                                default_wifi_tap_name.c_str(),
                                google::FlagSettingMode::SET_FLAGS_DEFAULT);
+  auto default_vlan_tap_name = GetPerInstanceDefault("cvd-net-");
+  SetCommandLineOptionWithMode("vlan_tap_name",
+                               default_vlan_tap_name.c_str(),
+                               google::FlagSettingMode::SET_FLAGS_DEFAULT);
   auto default_instance_dir =
       cvd::StringFromEnv("HOME", ".") + "/cuttlefish_runtime";
   SetCommandLineOptionWithMode("instance_dir",
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index d1507f8..9c9325e 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -108,6 +108,8 @@
 const char* kMobileTapName = "mobile_tap_name";
 const char* kWifiBridgeName = "wifi_bridge_name";
 const char* kWifiTapName = "wifi_tap_name";
+const char* kVlanTapName = "vlan_tap_name";
+const char* kGuestVlansName = "guest_vlans";
 const char* kWifiGuestMacAddr = "wifi_guest_mac_addr";
 const char* kWifiHostMacAddr = "wifi_host_mac_addr";
 const char* kEntropySource = "entropy_source";
@@ -428,6 +430,20 @@
   (*dictionary_)[kWifiTapName] = wifi_tap_name;
 }
 
+bool CuttlefishConfig::guest_vlans() const {
+  return (*dictionary_)[kGuestVlansName].asBool();
+}
+void CuttlefishConfig::set_guest_vlans(bool guest_vlans) {
+  (*dictionary_)[kGuestVlansName] = guest_vlans;
+}
+
+std::string CuttlefishConfig::vlan_tap_name() const {
+  return (*dictionary_)[kVlanTapName].asString();
+}
+void CuttlefishConfig::set_vlan_tap_name(const std::string& vlan_tap_name) {
+  (*dictionary_)[kVlanTapName] = vlan_tap_name;
+}
+
 std::string CuttlefishConfig::entropy_source() const {
   return (*dictionary_)[kEntropySource].asString();
 }
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index cbe280d..e48ab50 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -162,6 +162,12 @@
   std::string wifi_tap_name() const;
   void set_wifi_tap_name(const std::string& wifi_tap_name);
 
+  std::string vlan_tap_name() const;
+  void set_vlan_tap_name(const std::string& vlan_tap_name);
+
+  bool guest_vlans() const;
+  void set_guest_vlans(bool guest_vlans);
+
   std::string wifi_guest_mac_addr() const;
   void set_wifi_guest_mac_addr(const std::string& wifi_guest_mac_addr);
 
diff --git a/host/libs/vm_manager/cf_qemu.sh b/host/libs/vm_manager/cf_qemu.sh
index ee46ef2..8953b5d 100755
--- a/host/libs/vm_manager/cf_qemu.sh
+++ b/host/libs/vm_manager/cf_qemu.sh
@@ -23,12 +23,14 @@
         echo "01"
     fi
 }
+pci_index=1
 CUTTLEFISH_INSTANCE="${CUTTLEFISH_INSTANCE:-$(default_instance_number)}"
 default_instance_name="cvd-${CUTTLEFISH_INSTANCE}"
 default_uuid="699acfc4-c8c4-11e7-882b-5065f31dc1${CUTTLEFISH_INSTANCE}"
 default_dir="${HOME}/cuttlefish_runtime"
 default_mobile_tap_name="cvd-mtap-${CUTTLEFISH_INSTANCE}"
 default_wifi_tap_name="cvd-wtap-${CUTTLEFISH_INSTANCE}"
+default_vlan_tap_name="cvd-net-${CUTTLEFISH_INSTANCE}"
 
 if [[ -z "${ivshmem_vector_count}" ]]; then
     echo "The required ivshmem_vector_count environment variable is not set" >&2
@@ -54,29 +56,41 @@
     -kernel "${kernel_image_path:-${HOME}/kernel}"
     -append "${kernel_cmdline:-"loop.max_part=7 console=ttyS0 androidboot.console=ttyS1 androidboot.hardware=vsoc enforcing=0 audit=1 androidboot.selinux=permissive mac80211_hwsim.radios=0 security=selinux buildvariant=userdebug  androidboot.serialno=CUTTLEFISHCVD01 androidboot.lcd_density=160"}"
     -dtb "${dtb_path:-${HOME}/config/cuttlefish.dtb}"
-    -device "piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2"
-    -device "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x4"
-    -drive "file=${system_image_path:-${HOME}/system.img},format=raw,if=none,id=drive-virtio-disk0,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1"
-    -drive "file=${data_image_path:-${HOME}/userdata.img},format=raw,if=none,id=drive-virtio-disk1,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk1,id=virtio-disk1"
-    -drive "file=${cache_image_path:-${HOME}/cache.img},format=raw,if=none,id=drive-virtio-disk2,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x7,drive=drive-virtio-disk2,id=virtio-disk2"
-    -drive "file=${vendor_image_path:-${HOME}/vendor.img},format=raw,if=none,id=drive-virtio-disk3,aio=threads"
-    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=0x8,drive=drive-virtio-disk3,id=virtio-disk3"
+    -device "piix3-usb-uhci,id=usb,bus=pci.0,addr=$((pci_index++)).${pci_index}"
+)
+if [ "${guest_vlans:-false}" = true ] ; then
+  args+=(
+    -netdev "tap,id=hostnet0,ifname=${vlan_tap_name:-${default_vlan_tap_name}},script=no,downscript=no"
+    -device "virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=$((pci_index++))"
+  )
+else
+  args+=(
     -netdev "tap,id=hostnet0,ifname=${wifi_tap_name:-${default_wifi_tap_name}},script=no,downscript=no"
-    -device "virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=0x2"
+    -device "virtio-net-pci,netdev=hostnet0,id=net0,bus=pci.0,addr=$((pci_index++))"
     -netdev "tap,id=hostnet1,ifname=${mobile_tap_name:-${default_mobile_tap_name}},script=no,downscript=no"
-    -device "virtio-net-pci,netdev=hostnet1,id=net1,bus=pci.0,addr=0x3"
+    -device "virtio-net-pci,netdev=hostnet1,id=net1,bus=pci.0,addr=$((pci_index++))"
+  )
+fi
+
+args+=(
+    -device "virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=$((pci_index++))"
+    -drive "file=${system_image_path:-${HOME}/system.img},format=raw,if=none,id=drive-virtio-disk0,aio=threads"
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=$((pci_index++)),drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1"
+    -drive "file=${data_image_path:-${HOME}/userdata.img},format=raw,if=none,id=drive-virtio-disk1,aio=threads"
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=$((pci_index++)),drive=drive-virtio-disk1,id=virtio-disk1"
+    -drive "file=${cache_image_path:-${HOME}/cache.img},format=raw,if=none,id=drive-virtio-disk2,aio=threads"
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=$((pci_index++)),drive=drive-virtio-disk2,id=virtio-disk2"
+    -drive "file=${vendor_image_path:-${HOME}/vendor.img},format=raw,if=none,id=drive-virtio-disk3,aio=threads"
+    -device "virtio-blk-pci,scsi=off,bus=pci.0,addr=$((pci_index++)),drive=drive-virtio-disk3,id=virtio-disk3"
     -chardev "socket,id=charserial0,path=${kernel_log_socket_name:-${default_dir}/kernel-log}"
     -device "isa-serial,chardev=charserial0,id=serial0"
     -chardev "socket,id=charserial1,path=${console_path:-${default_dir}/console},server,nowait"
     -device "isa-serial,chardev=charserial1,id=serial1"
     -chardev "file,id=charchannel0,path=${logcat_path:-${default_dir}/logcat},append=on"
     -device "virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=cf-logcat"
-    -device "virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x9"
+    -device "virtio-balloon-pci,id=balloon0,bus=pci.0,addr=$((pci_index++))"
     -object "rng-random,id=objrng0,filename=/dev/urandom"
-    -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000,bus=pci.0,addr=0xa"
+    -device "virtio-rng-pci,rng=objrng0,id=rng0,max-bytes=1024,period=2000,bus=pci.0,addr=$((pci_index++))"
     -chardev "socket,path=${ivshmem_qemu_socket_path:-${default_dir}/ivshmem_socket_qemu},id=ivsocket"
     -device "ivshmem-doorbell,chardev=ivsocket,vectors=${ivshmem_vector_count}"
     -cpu host
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 7d7d92b..64b15d8 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -74,6 +74,8 @@
   LogAndSetEnv("cache_image_path", config->cache_image_path());
   LogAndSetEnv("vendor_image_path", config->vendor_image_path());
   LogAndSetEnv("wifi_tap_name", config->wifi_tap_name());
+  LogAndSetEnv("vlan_tap_name", config->vlan_tap_name());
+  LogAndSetEnv("guest_vlans", config->guest_vlans() ? "true" : "false");
   LogAndSetEnv("mobile_tap_name", config->mobile_tap_name());
   LogAndSetEnv("kernel_log_socket_name",
                       config->kernel_log_socket_name());