virtio_net
Change-Id: I9253d3eac86b4697f61bc16961b64aa32bac6f52
diff --git a/src/virtualization/bin/vmm/device/BUILD.gn b/src/virtualization/bin/vmm/device/BUILD.gn
index 6e3bba9..6394976 100644
--- a/src/virtualization/bin/vmm/device/BUILD.gn
+++ b/src/virtualization/bin/vmm/device/BUILD.gn
@@ -13,8 +13,8 @@
":virtio_gpu",
":virtio_input",
":virtio_magma",
- ":virtio_net",
":virtio_wl",
+ "virtio_net",
"virtio_rng",
]
}
@@ -248,30 +248,6 @@
deps = [ ":virtio_magma_bin_mock_system" ]
}
-executable("virtio_net_bin") {
- visibility = [ ":*" ]
-
- output_name = "virtio_net"
- sources = [
- "guest_ethernet.cc",
- "guest_ethernet.h",
- "virtio_net.cc",
- ]
- deps = [
- ":virtio",
- "//sdk/fidl/fuchsia.hardware.ethernet",
- "//sdk/fidl/fuchsia.netstack",
- "//sdk/lib/fit-promise",
- "//src/connectivity/network/lib/net_interfaces/cpp",
- "//zircon/system/ulib/async:async-cpp",
- ]
-}
-
-fuchsia_package_with_single_component("virtio_net") {
- manifest = "../meta/virtio_net.cmx"
- deps = [ ":virtio_net_bin" ]
-}
-
executable("virtio_wl_bin") {
visibility = [ ":*" ]
diff --git a/src/virtualization/bin/vmm/device/virtio_net/BUILD.gn b/src/virtualization/bin/vmm/device/virtio_net/BUILD.gn
new file mode 100644
index 0000000..939b186
--- /dev/null
+++ b/src/virtualization/bin/vmm/device/virtio_net/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright 2021 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/components.gni")
+import("//build/rust/rustc_binary.gni")
+import("//build/rust/rustc_library.gni")
+
+rustc_binary("virtio_net_bin") {
+ name = "virtio_net"
+ edition = "2018"
+
+ sources = [ "src/main.rs" ]
+
+ deps = [
+ "//sdk/fidl/fuchsia.hardware.ethernet:fuchsia.hardware.ethernet-rustc",
+ "//sdk/fidl/fuchsia.net:fuchsia.net-rustc",
+ "//sdk/fidl/fuchsia.net.interfaces:fuchsia.net.interfaces-rustc",
+ "//sdk/fidl/fuchsia.netstack:fuchsia.netstack-rustc",
+ "//sdk/fidl/fuchsia.virtualization.hardware:fuchsia.virtualization.hardware-rustc",
+ "//src/lib/async-utils",
+ "//src/lib/fidl/rust/fidl",
+ "//third_party/rust_crates:libc",
+ "//src/lib/fuchsia-async",
+ "//src/lib/fuchsia-component",
+ "//src/lib/fuchsia-runtime",
+ "//src/lib/syslog",
+ "//src/lib/zerocopy",
+ "//src/lib/zircon/rust:fuchsia-zircon",
+ "//src/virtualization/lib/machina-virtio-device",
+ "//src/virtualization/lib/virtio-device",
+ "//third_party/rust_crates:crossbeam",
+ "//third_party/rust_crates:futures",
+ "//third_party/rust_crates:parking_lot",
+ "//third_party/rust_crates:pin-utils",
+ "//third_party/rust_crates:anyhow",
+ "//third_party/rust_crates:matches",
+ "//src/lib/network/fidl_fuchsia_net_interfaces_ext",
+ "//src/lib/network/ethernet",
+ ]
+}
+
+fuchsia_package_with_single_component("virtio_net") {
+ manifest = "../../meta/virtio_net.cmx"
+ deps = [ ":virtio_net_bin" ]
+}
diff --git a/src/virtualization/bin/vmm/device/virtio_net/src/main.rs b/src/virtualization/bin/vmm/device/virtio_net/src/main.rs
new file mode 100644
index 0000000..769b8da
--- /dev/null
+++ b/src/virtualization/bin/vmm/device/virtio_net/src/main.rs
@@ -0,0 +1,527 @@
+// Copyright 2021 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#![allow(warnings)]
+
+use {
+ fidl::{
+ encoding::Decodable,
+ endpoints::{self as endpoints, RequestStream, ServiceMarker},
+ },
+ async_utils::hanging_get::client::HangingGetStream,
+ anyhow::{anyhow, Context as AnyhowContext},
+ fidl_fuchsia_hardware_ethernet::{self as hardware_ethernet},
+ fidl_fuchsia_net::{self as net},
+ fidl_fuchsia_net_interfaces::{self as net_interfaces},
+ fidl_fuchsia_net_interfaces_ext::{self as net_interfaces_ext},
+ fidl_fuchsia_netstack::{self as netstack, NetstackProxy},
+ fidl_fuchsia_virtualization_hardware::{
+ StartInfo, VirtioDeviceRequest, VirtioDeviceRequestStream, VirtioNetMarker,
+ VirtioNetRequest, VirtioNetRequestStream, EVENT_SET_INTERRUPT, EVENT_SET_QUEUE,
+ },
+ fuchsia_async::{
+ self as fasync, Fifo, FifoEntry, FifoReadable, FifoWritable, PacketReceiver,
+ ReceiverRegistration,
+ },
+ fuchsia_component::client::connect_to_protocol,
+ std::convert::{TryInto,TryFrom},
+ fuchsia_component::server,
+ fuchsia_component::server::ServiceFs,
+ fuchsia_syslog::{self as syslog, fx_log_err, fx_log_info},
+ fuchsia_zircon::{self as zx, AsHandleRef},
+ futures::{
+ channel::mpsc,
+ select,
+ task::{noop_waker, noop_waker_ref, AtomicWaker},
+ Future, FutureExt, Sink, SinkExt, Stream, StreamExt, TryFutureExt, TryStreamExt,
+ },
+ parking_lot::Mutex,
+ pin_utils::{pin_mut},
+ std::{
+ collections::HashMap,
+ io::{Read, Write},
+ mem,
+ ops::{Deref, DerefMut},
+ pin::Pin,
+ sync::atomic,
+ task::{Context, Poll},
+ },
+ virtio_device::{
+ chain::{ReadableChain, WritableChain},
+ util::BufferedNotify,
+ queue::DriverNotify,
+ },
+ machina_virtio_device::*,
+ zerocopy,
+ zerocopy::AsBytes,
+ matches::matches,
+ ethernet::{ethernet_sys::*},
+};
+
+struct GuestEthernet {
+ rx_fifo: Fifo<EthernetFifoEntry>,
+ tx_fifo: Fifo<EthernetFifoEntry>,
+ vmo: zx::Vmo,
+ io_mem_base: *mut u8,
+ io_mem_len: usize,
+ con: fidl_fuchsia_hardware_ethernet::DeviceRequestStream,
+}
+
+const MTU: u32 = 1500;
+// This is a locally administered MAC address (first byte 0x02) mixed with the
+// Google Organizationally Unique Identifier (00:1a:11). The host gets ff:ff:ff
+// and the guest gets 00:00:00 for the last three octets.
+const HOST_MAC_ADDRESS: hardware_ethernet::MacAddress =
+ hardware_ethernet::MacAddress { octets: [0x02, 0x1a, 0x11, 0xff, 0xff, 0xff] };
+
+const VIRTIO_NET_QUEUE_SIZE: usize = 256;
+
+// Copied from //zircon/system/public/zircon/device/ethernet.h
+// const ETH_FIFO_RX_OK: u16 = 1; // packet received okay
+// const ETH_FIFO_TX_OK: u16 = 1; // packet transmitted okay
+// const ETH_FIFO_INVALID: u16 = 2; // packet not within io_vmo bounds
+// const ETH_FIFO_RX_TX: u16 = 4; // received our own tx packet (when TX_LISTEN)
+
+#[repr(transparent)]
+pub struct EthernetFifoEntry(eth_fifo_entry);
+
+unsafe impl FifoEntry for EthernetFifoEntry {}
+
+impl GuestEthernet {
+ async fn new(
+ mut con: fidl_fuchsia_hardware_ethernet::DeviceRequestStream,
+ ) -> Result<GuestEthernet, anyhow::Error> {
+ // start the message loop
+ let mut fifo = None;
+ let mut vmo = None;
+ loop {
+ match con.next().await {
+ Some(Ok(hardware_ethernet::DeviceRequest::GetInfo { responder })) => {
+ // TODO: use proper mac address
+ let mut info = hardware_ethernet::Info {
+ features: hardware_ethernet::Features::Synthetic,
+ mtu: MTU,
+ mac: HOST_MAC_ADDRESS,
+ };
+ responder.send(&mut info);
+ }
+ Some(Ok(hardware_ethernet::DeviceRequest::GetFifos { responder })) => {
+ if fifo.is_some() {
+ panic!("Duplicated fifos");
+ }
+ // TODO: should send an error on failures instead of dropping responder?
+ let (rx_a, rx_b) = zx::Fifo::create(
+ VIRTIO_NET_QUEUE_SIZE,
+ mem::size_of::<EthernetFifoEntry>(),
+ )?;
+ let (tx_a, tx_b) = zx::Fifo::create(
+ VIRTIO_NET_QUEUE_SIZE,
+ mem::size_of::<EthernetFifoEntry>(),
+ )?;
+ let mut fifos = hardware_ethernet::Fifos {
+ rx: rx_a,
+ tx: tx_b,
+ rx_depth: VIRTIO_NET_QUEUE_SIZE as u32,
+ tx_depth: VIRTIO_NET_QUEUE_SIZE as u32,
+ };
+ let rx = Fifo::from_fifo(rx_b)?;
+ let tx = Fifo::from_fifo(tx_a)? ;
+
+ responder.send(zx::Status::OK.into_raw(), Some(&mut fifos));
+ fifo = Some((tx, rx));
+ }
+ Some(Ok(hardware_ethernet::DeviceRequest::SetIoBuffer { h, responder })) => {
+ if vmo.is_some() {
+ panic!("Duplicated vmo");
+ }
+ let vmo_size = h.get_size()? as usize;
+ let addr = fuchsia_runtime::vmar_root_self()
+ .map(
+ 0,
+ &h,
+ 0,
+ vmo_size,
+ zx::VmarFlags::PERM_READ
+ | zx::VmarFlags::PERM_WRITE
+ | zx::VmarFlags::REQUIRE_NON_RESIZABLE,
+ )?;
+ responder.send(zx::Status::OK.into_raw());
+ vmo = Some((h, addr, vmo_size));
+ }
+ Some(Ok(hardware_ethernet::DeviceRequest::Start { responder })) => match (fifo, vmo) {
+ (Some((tx_fifo, rx_fifo)), Some((vmo, io_addr, io_size))) => {
+ responder.send(zx::Status::OK.into_raw());
+ return Ok(GuestEthernet { tx_fifo, rx_fifo, vmo, io_mem_base: io_addr as usize as *mut u8, io_mem_len: io_size, con });
+ }
+ _ => panic!("Start called too soon"),
+ },
+ Some(Ok(hardware_ethernet::DeviceRequest::SetClientName { name, responder })) => {
+ println!("Name set to {}", name);
+ responder.send(zx::Status::OK.into_raw());
+ }
+ Some(Ok(msg)) => return Err(anyhow!("Unknown msg")),
+ Some(Err(e)) => return Err(anyhow!("Some fidl error")),
+ None => return Err(anyhow!("Unexpected end of stream")),
+ }
+ }
+ }
+ pub fn rx_packet_stream<'a>(
+ &'a self,
+ ) -> PacketStream<'a, RxPacket> {
+ let mut stream = EntryStream::new(&self.rx_fifo);
+ PacketStream { stream, io_mem_base: self.io_mem_base, io_mem_len: self.io_mem_len,packet_type: std::marker::PhantomData }
+ }
+ pub fn tx_packet_stream<'a>(
+ &'a self,
+ ) -> PacketStream<'a, TxPacket> {
+ let mut stream = EntryStream::new(&self.tx_fifo);
+ PacketStream { stream, io_mem_base: self.io_mem_base, io_mem_len: self.io_mem_len,packet_type: std::marker::PhantomData }
+ }
+}
+
+/// EntryStream represents the stream of reads from the fifo.
+pub struct EntryStream<'a, F, R: 'a> {
+ fifo: &'a F,
+ read_marker: ::std::marker::PhantomData<R>,
+}
+
+impl<'a, F: FifoReadable<R>, R: FifoEntry> EntryStream<'a, F, R> {
+ /// Create a new EntryStream, which borrows the `FifoReadable` type
+ /// until the stream completes.
+ pub fn new(fifo: &'a F) -> EntryStream<'_, F, R> {
+ EntryStream { fifo, read_marker: ::std::marker::PhantomData }
+ }
+}
+
+impl<'a, F: FifoReadable<R>, R: FifoEntry> Stream for EntryStream<'a, F, R> {
+ type Item = Result<R, zx::Status>;
+
+ fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ self.fifo.read(cx).map(|v| v.transpose())
+ }
+}
+
+struct PacketStream<'a, P> {
+ stream: EntryStream<
+ 'a,
+ Fifo<EthernetFifoEntry>,
+ EthernetFifoEntry,
+ >,
+ io_mem_base: *mut u8,
+ io_mem_len: usize,
+ packet_type: std::marker::PhantomData<P>,
+}
+
+impl<'a, P> Unpin for PacketStream<'a, P> {}
+
+impl<'a, P: PacketFlags> Stream for PacketStream<'a, P> {
+ type Item = PacketEntry<'a, P>;
+
+ fn poll_next(mut self: Pin<&mut Self>, lw: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ match self.stream.poll_next_unpin(lw) {
+ Poll::Ready(Some(Err(e))) => {
+ panic!("Cannot handle errors");
+ }
+ Poll::Ready(Some(Ok(mut entry))) => {
+ // todo overflow
+ if entry.0.offset as usize + entry.0.length as usize > self.io_mem_len {
+ panic!("invalid");
+ }
+ Poll::Ready(Some(PacketEntry { packet: unsafe{self.io_mem_base.add(entry.0.offset as usize)}, entry, fifo : self.stream.fifo, packet_type: std::marker::PhantomData}))
+ }
+ Poll::Ready(None) => Poll::Ready(None),
+ Poll::Pending => Poll::Pending,
+ }
+ }
+}
+
+
+trait PacketFlags {
+ fn okay() -> u32;
+ fn invalid() -> u32 {
+ ETH_FIFO_INVALID
+ }
+}
+
+struct RxPacket {}
+struct TxPacket {}
+
+impl PacketFlags for RxPacket {
+ fn okay() -> u32 {
+ ETH_FIFO_RX_OK
+ }
+}
+
+impl PacketFlags for TxPacket {
+ fn okay() -> u32 {
+ ETH_FIFO_TX_OK
+ }
+}
+
+struct PacketEntry<'a, P> {
+ packet: *mut u8,
+ entry: EthernetFifoEntry,
+ fifo: &'a Fifo<EthernetFifoEntry, EthernetFifoEntry>,
+ packet_type: std::marker::PhantomData<P>,
+}
+
+impl<'a, P:PacketFlags> PacketEntry<'a, P> {
+ pub async fn okay(self) -> Result<(), fidl::Status>{
+ let PacketEntry { packet:_, mut entry, fifo, packet_type:_ } = self;
+ entry.0.flags = P::okay() as u16;
+ fifo.write_entries(std::slice::from_ref(&entry)).await
+ }
+ pub async fn cancel(self) -> Result<(), fidl::Status>{
+ let PacketEntry { packet:_, mut entry, fifo, packet_type:_ } = self;
+ entry.0.flags = P::invalid() as u16;
+ fifo.write_entries(std::slice::from_ref(&entry)).await
+ }
+ pub fn len(&self) -> usize {
+ self.entry.0.length as usize
+ }
+ pub fn as_ptr(&self) -> *const u8 {
+ self.packet
+ }
+}
+
+impl<'a> PacketEntry<'a, RxPacket> {
+ pub fn set_length(mut self, len: usize) -> Result<PacketEntry<'a, RxPacket>, PacketEntry<'a, RxPacket>> {
+ if len <= self.entry.0.length as usize {
+ self.entry.0.length = len as u16;
+ Ok(self)
+ } else {
+ Err(self)
+ }
+ }
+ pub fn as_mut_ptr(&mut self) -> *mut u8 {
+ self.packet
+ }
+}
+
+// const INTERFACE_PATH: &'static str = "/dev/class/ethernet/virtio";
+const INTERFACE_NAME: &'static str = "ethv0";
+// const IPV4_ADDRESS: net::Ipv4Address = net::Ipv4Address{addr: [10, 0, 0, 1]};
+
+#[repr(C, packed)]
+#[derive(Debug, Default, Clone, zerocopy::FromBytes, zerocopy::AsBytes)]
+pub struct VirtioNetHdr {
+ flags: u8,
+ gso_type: u8,
+ hdr_len: u16,
+ gso_size: u16,
+ csum_start: u16,
+ csum_offset: u16,
+ // Only if |VIRTIO_NET_F_MRG_RXBUF| or |VIRTIO_F_VERSION_1|.
+ num_buffers: u16,
+}
+
+struct VirtioNetPacket<B> {
+ header: zerocopy::LayoutVerified<B, VirtioNetHdr>,
+ body: B,
+}
+
+#[repr(u16)]
+enum NetQueues {
+ RX = 0,
+ TX = 1,
+}
+
+struct StreamNotifyOnPending<S, N: DriverNotify> {
+ stream: S,
+ notify: BufferedNotify<N>,
+ was_ready: bool,
+}
+
+impl<S: Unpin, N:DriverNotify> Unpin for StreamNotifyOnPending<S, N> {}
+
+impl<S, N: DriverNotify> StreamNotifyOnPending<S, N> {
+ pub fn from_stream(stream: S, notify: BufferedNotify<N>) -> Self {
+ Self {stream, notify, was_ready: true}
+ }
+}
+
+impl<S: Stream<Item=I> + Unpin, I, N: DriverNotify> Stream for StreamNotifyOnPending<S, N> {
+ type Item=I;
+ fn poll_next(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>
+ ) -> Poll<Option<Self::Item>> {
+ let result = self.stream.poll_next_unpin(cx);
+ let was_ready = result.is_ready();
+ if !was_ready && self.was_ready {
+ self.notify.flush();
+ }
+ self.was_ready = was_ready;
+ result
+ }
+}
+
+async fn run_virtio_net(mut con: VirtioNetRequestStream) -> Result<(), anyhow::Error> {
+ // Expect a start message first off to configure things.
+ let (start_info, mac_address, enable_bridge, responder) = con
+ .try_next()
+ .await?
+ .ok_or(anyhow!("Unexpected end of stream"))?
+ .into_start()
+ .ok_or(anyhow!("Expected Start message"))?;
+
+ // Connect to the host nestack
+ let netstack = connect_to_protocol::<netstack::NetstackMarker>().context("failed to connect to netstack")?;
+ let net_interfaces = connect_to_protocol::<net_interfaces::StateMarker>().context("failed to connect to net_interfaces")?;
+ let (watcher, watcher_server) = fidl::endpoints::create_proxy()?;
+ net_interfaces
+ .get_watcher(net_interfaces::WatcherOptions::EMPTY, watcher_server)?;
+ let mut watch_stream = HangingGetStream::new(Box::new(move || {Some(watcher.watch())}));
+
+ let mut config = netstack::InterfaceConfig::new_empty();
+ config.name = INTERFACE_NAME.into(); /*, ip_address_config:
+ netstack::IpAddressConfig::StaticIp(net::Subnet{addr: net::IpAddress::Ipv4(IPV4_ADDRESS), prefix_len: 24})};*/
+
+ let (device_client, device_server) =
+ endpoints::create_endpoints::<hardware_ethernet::DeviceMarker>()?;
+ let device_server = device_server.into_stream()?;
+
+ let ethernet_start_future =
+ GuestEthernet::new(device_server /*, INTERFACE_PATH, INTERFACE_NAME, IPV4_ADDRESS*/);
+
+ // Add ourselves as an ethernet device to the netstack
+ let add_device_future = async {
+ let id = netstack
+ .add_ethernet_device("", &mut config, device_client)
+ .await?.map_err(|_| anyhow!("todo"))?;
+ netstack.set_interface_status(id, true)?;
+ if enable_bridge {
+ let interface_id = watch_stream.err_into::<anyhow::Error>()
+ // Make it a stream of just properties that generates an error if we hit Idle
+ .try_filter_map(|x|
+ match x {
+ net_interfaces::Event::Existing(prop) | net_interfaces::Event::Added(prop)
+ =>
+ futures::future::ok(Some(prop)),
+ net_interfaces::Event::Idle(_) => futures::future::err(anyhow!("Failed to bridge")),
+ _ => futures::future::ok(None)
+ })
+ // Convert the properties to the expected form
+ .and_then(|x| futures::future::ready(x.try_into().map_err(std::convert::Into::into)))
+ // Filter for globally reachable interfaces.
+ .try_filter_map(|prop| futures::future::ok(if net_interfaces_ext::is_globally_routable(&prop) { Some(prop.id) } else {None}))
+ .try_next().await?.ok_or(anyhow!("Unexected end of stream"))?;
+
+ let bridge_result =
+ netstack.bridge_interfaces(&[interface_id as u32, id]).await?;
+ if bridge_result.0.status != netstack::Status::Ok {
+ return Err(anyhow!("Some bridge problem"));
+ }
+ netstack.set_interface_status(bridge_result.1, true)?;
+ }
+ Ok::<(), anyhow::Error>(())
+ };
+
+ let (device_builder, guest_mem) = from_start_info(start_info)?;
+ responder.send();
+
+ let mut con = con.cast_stream();
+ let device_future = config_builder_from_stream(
+ device_builder.map_notify(|e| Ok(BufferedNotify::new(e)))?,
+ &mut con,
+ &[NetQueues::RX as u16, NetQueues::TX as u16][..],
+ &guest_mem,
+ ).err_into::<anyhow::Error>();
+
+ let (guest_ethernet, (), (mut device, ready_responder)) = futures::future::try_join3(
+ ethernet_start_future,
+ add_device_future,
+ device_future,
+ )
+ .await?;
+
+ let mut tx_stream = device.take_stream(NetQueues::TX as u16)?;
+ let mut rx_stream = device.take_stream(NetQueues::RX as u16)?;
+
+ ready_responder.send().unwrap();
+
+ let mut rx_packet_stream = guest_ethernet.rx_packet_stream();
+ let mut tx_packet_stream = guest_ethernet.tx_packet_stream();
+
+ let mut rx_fut_stream = StreamNotifyOnPending::from_stream(rx_packet_stream.zip(tx_stream), device.get_notify().clone());
+
+ // Wait for some tx
+ let rx_fut = async {
+ while let Some((mut packet, mut desc_chain)) = rx_fut_stream.next().await {
+ let mut chain = ReadableChain::new(desc_chain, &guest_mem);
+ let mut header = VirtioNetHdr::default();
+ chain
+ .read_exact(header.as_bytes_mut())?;
+
+ // TODO: better rxpacket lifecycle.
+ match packet.set_length(chain.remaining()?) {
+ Ok(mut packet) => {
+ let mut offset: usize = 0;
+ while let Some(Ok(range)) = chain.next() {
+ // TODO: validate lengths
+ unsafe{libc::memmove(packet.as_mut_ptr().add(offset)as *mut libc::c_void, range.try_ptr().ok_or(anyhow!("bad ptr"))?, range.len())};
+ offset += range.len();
+ }
+ chain.return_complete()?;
+ packet.okay().await
+ },
+ Err(packet) => packet.cancel().await
+ }?;
+ }
+ Err::<(),_>(anyhow!("Unexpected end of stream"))
+ };
+
+ let mut tx_fut_stream = StreamNotifyOnPending::from_stream(tx_packet_stream.zip(rx_stream), device.get_notify().clone());
+ let tx_fut = async {
+ while let Some((mut packet, mut desc_chain)) = tx_fut_stream.next().await {
+ let mut chain = WritableChain::new(desc_chain, &guest_mem)?;
+ chain
+ .write_all(
+ VirtioNetHdr {
+ num_buffers: 1,
+ gso_type: 0,
+ gso_size: 0,
+ flags: 0,
+ hdr_len: 0,
+ csum_start: 0,
+ csum_offset: 0,
+ }
+ .as_bytes(),
+ )?;
+ let mut offset: usize = 0;
+ while let Some(Ok(range)) = chain.next() {
+ // TODO: validate lengths
+ unsafe{libc::memmove(range.try_mut_ptr().ok_or(anyhow!("bad ptr"))?, packet.as_ptr().add(offset) as *const libc::c_void, range.len())};
+ offset += range.len();
+ }
+ packet.okay().await?;
+ };
+ Err::<(),_>(anyhow!("Unexpected end of stream"))
+ };
+
+ futures::future::try_join3(
+ device.run_device_notify(con).err_into::<anyhow::Error>(),
+ rx_fut,
+ tx_fut,
+ )
+ .await?;
+
+ Ok(())
+}
+
+#[fasync::run_singlethreaded]
+async fn main() -> Result<(), anyhow::Error> {
+ syslog::init().context("Unable to initialize syslog")?;
+ let mut fs = server::ServiceFs::new();
+ fs.dir("svc").add_fidl_service(|stream: VirtioNetRequestStream| stream);
+ fs.take_and_serve_directory_handle().context("Error starting server")?;
+
+ fs.for_each_concurrent(None, |stream| async {
+ if let Err(e) = run_virtio_net(stream).await {
+ syslog::fx_log_err!("Error {} running virtio_net service", e);
+ }
+ }).await;
+
+ Ok(())
+}