[virtualization] Use bridge in virtio-net
Without bridging interfaces in virtio-net, we can not correctly send or
receive data from the outside world.
Note, bridging is broken in netstack and must be fixed there too.
Change-Id: I64dbcfa399a5d4aab9ca0af0fd8eb9a3344c78a2
diff --git a/src/virtualization/bin/vmm/device/virtio_net.cc b/src/virtualization/bin/vmm/device/virtio_net.cc
index 57697f2..e13b9e7 100644
--- a/src/virtualization/bin/vmm/device/virtio_net.cc
+++ b/src/virtualization/bin/vmm/device/virtio_net.cc
@@ -25,10 +25,7 @@
#include "src/virtualization/bin/vmm/device/device_base.h"
#include "src/virtualization/bin/vmm/device/stream_base.h"
-static constexpr char kInterfacePath[] = "/dev/class/ethernet/virtio";
static constexpr char kInterfaceName[] = "ethv0";
-static constexpr uint8_t kIpv4Address[4] = {10, 0, 0, 1};
-static constexpr uint8_t kPrefixLength = 24;
enum class Queue : uint16_t {
RECEIVE = 0,
@@ -139,9 +136,8 @@
if (desc_.has_next) {
// Section 5.1.6.2 Packet Transmission: The header and packet are added
// as one output descriptor to the transmitq.
- static bool warned = false;
- if (!warned) {
- warned = true;
+ if (!warned_) {
+ warned_ = true;
FX_LOGS(WARNING) << "Transmit packet and header must be on a single descriptor";
}
continue;
@@ -163,6 +159,7 @@
private:
GuestEthernet* guest_ethernet_ = nullptr;
const PhysMem* phys_mem_ = nullptr;
+ bool warned_ = false;
};
class VirtioNetImpl : public DeviceBase<VirtioNetImpl>,
@@ -210,65 +207,84 @@
mac_address_ = std::move(mac_address);
- fuchsia::net::Ipv4Address ipv4;
- memcpy(ipv4.addr.data(), kIpv4Address, 4);
-
- fuchsia::net::IpAddress addr;
- addr.set_ipv4(ipv4);
-
- fuchsia::net::Subnet subnet;
- subnet.addr.set_ipv4(ipv4);
- subnet.prefix_len = kPrefixLength;
-
- fuchsia::netstack::InterfaceConfig config;
- config.name = kInterfaceName;
+ auto guest_interface =
+ CreateGuestInterface().and_then(fit::bind_member(this, &VirtioNetImpl::EnableInterface));
executor_.schedule_task(
- AddEthernetDevice(kInterfacePath, std::move(config))
- .and_then([this, addr = std::move(addr)](const uint32_t& nic_id) mutable {
- return SetInterfaceAddress(nic_id, std::move(addr));
- })
- .and_then([this](const uint32_t& nic_id) mutable {
- netstack_->SetInterfaceStatus(nic_id, true);
- })
- .or_else([]() mutable { FX_CHECK(false) << "Failed to set ethernet IP address."; })
- .and_then(std::move(callback))
+ fit::join_promises(FindHostInterface(), std::move(guest_interface))
+ .and_then(fit::bind_member(this, &VirtioNetImpl::CreateBridgeInterface))
+ .and_then(fit::bind_member(this, &VirtioNetImpl::EnableInterface))
+ .and_then([callback = std::move(callback)](const uint32_t& nic_id) { callback(); })
+ .or_else([] { FX_LOGS(FATAL) << "Failed to setup guest ethernet"; })
.wrap_with(scope_));
}
- fit::promise<uint32_t> AddEthernetDevice(const char* interface_path,
- fuchsia::netstack::InterfaceConfig config) {
+ fit::promise<uint32_t> CreateGuestInterface() {
fit::bridge<uint32_t> bridge;
- netstack_->AddEthernetDevice(
- interface_path, std::move(config), device_binding_.NewBinding(),
- [completer = std::move(bridge.completer)](
- fuchsia::netstack::Netstack_AddEthernetDevice_Result result) mutable {
- if (result.is_err()) {
- FX_LOGS(WARNING) << "Failed to add to Netstack: " << zx_status_get_string(result.err());
- completer.complete_error();
- } else {
- completer.complete_ok(result.response().ResultValue_());
- }
- });
+ fuchsia::netstack::InterfaceConfig config;
+ config.name = kInterfaceName;
+ auto callback = [completer = std::move(bridge.completer)](
+ fuchsia::netstack::Netstack_AddEthernetDevice_Result result) mutable {
+ if (result.is_err()) {
+ FX_LOGS(ERROR) << "Failed to create guest interface";
+ completer.complete_error();
+ } else {
+ completer.complete_ok(result.response().nicid);
+ }
+ };
+ netstack_->AddEthernetDevice("", std::move(config), device_binding_.NewBinding(),
+ std::move(callback));
return bridge.consumer.promise();
}
- fit::promise<uint32_t> SetInterfaceAddress(uint32_t nic_id, fuchsia::net::IpAddress addr) {
+ fit::promise<uint32_t> EnableInterface(const uint32_t& nic_id) {
+ netstack_->SetInterfaceStatus(nic_id, true);
+ return fit::make_ok_promise(nic_id);
+ }
+
+ fit::promise<uint32_t> FindHostInterface() {
fit::bridge<uint32_t> bridge;
- netstack_->SetInterfaceAddress(
- nic_id, std::move(addr), kPrefixLength,
- [nic_id, completer = std::move(bridge.completer)](fuchsia::netstack::NetErr err) mutable {
- if (err.status == fuchsia::netstack::Status::OK) {
- completer.complete_ok(nic_id);
- } else {
- FX_LOGS(ERROR) << "Failed to set interface address with "
- << static_cast<uint32_t>(err.status) << " " << err.message;
- completer.complete_error();
- }
- });
+ auto callback = [completer = std::move(bridge.completer)](
+ std::vector<fuchsia::netstack::NetInterface> interfaces) mutable {
+ for (auto& interface : interfaces) {
+ if (!(static_cast<uint32_t>(interface.flags) &
+ static_cast<uint32_t>(fuchsia::netstack::Flags::UP)) ||
+ static_cast<uint32_t>(interface.features) != 0) {
+ continue;
+ }
+ completer.complete_ok(interface.id);
+ return;
+ }
+ FX_LOGS(ERROR) << "Failed to find host interface";
+ completer.complete_error();
+ };
+ netstack_->GetInterfaces(std::move(callback));
+
+ return bridge.consumer.promise();
+ }
+
+ fit::promise<uint32_t> CreateBridgeInterface(
+ const std::tuple<fit::result<uint32_t>, fit::result<uint32_t>>& nic_ids) {
+ auto& [host_id, guest_id] = nic_ids;
+ if (host_id.is_error() || guest_id.is_error()) {
+ return fit::make_result_promise<uint32_t>(fit::error());
+ }
+ fit::bridge<uint32_t> bridge;
+
+ auto callback = [completer = std::move(bridge.completer)](fuchsia::netstack::NetErr result,
+ uint32_t nic_id) mutable {
+ if (result.status != fuchsia::netstack::Status::OK) {
+ FX_LOGS(ERROR) << "Failed to create bridge interface";
+ completer.complete_error();
+ } else {
+ completer.complete_ok(nic_id);
+ }
+ };
+ netstack_->BridgeInterfaces({host_id.value(), guest_id.value()}, std::move(callback));
+
return bridge.consumer.promise();
}
diff --git a/src/virtualization/bin/vmm/main.cc b/src/virtualization/bin/vmm/main.cc
index 8500a39..ed4ef2e 100644
--- a/src/virtualization/bin/vmm/main.cc
+++ b/src/virtualization/bin/vmm/main.cc
@@ -256,23 +256,6 @@
}
}
- // Setup net device.
- std::vector<std::unique_ptr<VirtioNet>> net_devices;
- for (auto net_device : cfg.net_devices()) {
- auto net = std::make_unique<VirtioNet>(guest.phys_mem());
- status = bus.Connect(net->pci_device(), device_loop.dispatcher(), true);
- if (status != ZX_OK) {
- return status;
- }
- status = net->Start(guest.object(), net_device.mac_address, launcher.get(),
- device_loop.dispatcher());
- if (status != ZX_OK) {
- FX_LOGS(INFO) << "Could not open Ethernet device " << status;
- return status;
- }
- net_devices.push_back(std::move(net));
- }
-
// Setup RNG device.
VirtioRng rng(guest.phys_mem());
if (cfg.virtio_rng()) {
@@ -372,6 +355,26 @@
}
}
+ // Setup net device.
+ // We setup networking last, as this can cause a temporary loss of network
+ // access as we configure the bridge. If networking is lost while loading
+ // packages for devices, the VMM will fail.
+ std::vector<std::unique_ptr<VirtioNet>> net_devices;
+ for (auto net_device : cfg.net_devices()) {
+ auto net = std::make_unique<VirtioNet>(guest.phys_mem());
+ status = bus.Connect(net->pci_device(), device_loop.dispatcher(), true);
+ if (status != ZX_OK) {
+ return status;
+ }
+ status = net->Start(guest.object(), net_device.mac_address, launcher.get(),
+ device_loop.dispatcher());
+ if (status != ZX_OK) {
+ FX_LOGS(INFO) << "Could not open Ethernet device " << status;
+ return status;
+ }
+ net_devices.push_back(std::move(net));
+ }
+
#if __x86_64__
status = create_page_table(guest.phys_mem());
if (status != ZX_OK) {