Add virtio net test util

This utility has two commands:
up - Brings up the ethernet interface.
xfer - Sends a UDP packet to the host, then waits for a reply.

TEST=guest_integration_tests

Change-Id: Ieaddfdb7a038286664c587bd835e1621ba3e6ef5
diff --git a/build.sh b/build.sh
index 3557af4..68ba5aa 100755
--- a/build.sh
+++ b/build.sh
@@ -36,8 +36,9 @@
 cargo build --target=${TARGET}
 
 mkdir -p ${OUT_DIR}
-cp target/${TARGET}/debug/virtio_rng_test_util ${OUT_DIR}
 cp target/${TARGET}/debug/virtio_block_test_util ${OUT_DIR}
+cp target/${TARGET}/debug/virtio_net_test_util ${OUT_DIR}
+cp target/${TARGET}/debug/virtio_rng_test_util ${OUT_DIR}
 
 declare -r BLOCK_SIZE=4096
 declare -r ADDITIONAL_BLOCKS=1024
diff --git a/src/bin/virtio_net_test_util.rs b/src/bin/virtio_net_test_util.rs
new file mode 100644
index 0000000..60945b1
--- /dev/null
+++ b/src/bin/virtio_net_test_util.rs
@@ -0,0 +1,106 @@
+// Copyright 2019 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.
+
+use libc::{self, c_char, c_int, c_short};
+use std::ffi::CString;
+use std::io::Error;
+use std::net::UdpSocket;
+use std::ptr;
+use structopt::StructOpt;
+
+// Socket ioctl get interface index. Defined in Linux:include/uapi/linux/sockios.h
+const SIOCSIFFLAGS: c_int = 0x8914;
+
+// Interface flag UP. Defined in Linux:include/uapi/linux/if.h
+const IFF_UP: c_short = 1 << 0;
+const IFF_NOARP: c_short = 1 << 7;
+
+const ETH_IFACE_NAME: &str = "eth0";
+
+#[derive(StructOpt, Debug)]
+enum Command {
+    #[structopt(name = "up")]
+    Up,
+    #[structopt(name = "xfer")]
+    Transfer {
+        send_byte: u8,
+        receive_byte: u8,
+        length: usize,
+    },
+}
+
+// Open a socket and return the file descriptor. The type and protocol don't really matter since
+// we're only using this socket to perform ioctls.
+fn open_fd() -> std::io::Result<i32> {
+    // The protocol is a two byte value that must be given as an i32 in network order. It is defined
+    // as an i32 in libc so we must cast to u16 before converting to big endian.
+    let protocol = (libc::IPPROTO_UDP as u16).to_be() as i32;
+    let fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_DGRAM, protocol) };
+    if fd == -1 {
+        Err(Error::last_os_error())
+    } else {
+        Ok(fd)
+    }
+}
+
+// See Linux:include/uapi/linux/if.h.
+// We add padding to this struct to bring the size to sizeof(ifreq), which is 40.
+#[repr(C)]
+struct SetFlagsRequest {
+    name: [c_char; 16],
+    flags: c_short,
+    _padding: [u8; 22],
+}
+
+// Do a "set interface flags" ioctl on the fd. We use this to bring up the ethernet interface, which
+// is specified in the request using ETH_IFACE_NAME.
+fn set_flags(fd: i32, flags: c_short) -> () {
+    let mut req = SetFlagsRequest {
+        name: [0; 16],
+        flags: flags,
+        _padding: [0; 22],
+    };
+    let name = CString::new(ETH_IFACE_NAME).unwrap();
+    unsafe {
+        ptr::copy_nonoverlapping(
+            name.as_ptr(),
+            req.name.as_mut_ptr(),
+            name.as_bytes_with_nul().len(),
+        );
+        libc::ioctl(fd, SIOCSIFFLAGS, &mut req);
+    }
+}
+
+fn main() -> std::io::Result<()> {
+    let cmd = Command::from_args();
+    match cmd {
+        Command::Up => {
+            let fd = open_fd()?;
+            // In addition to bringing up the interface (IFF_UP), we specify no ARP (IFF_NOARP) to
+            // reduce packet spam.
+            set_flags(fd, IFF_UP | IFF_NOARP);
+            println!("PASS");
+        }
+        Command::Transfer {
+            send_byte,
+            receive_byte,
+            length,
+        } => {
+            // Bind the socket to all addresses, on port 4242.
+            let socket = UdpSocket::bind(("::", 4242))?;
+
+            // Send to fe80::1, the IPv6 address of the host.
+            let send_buf = vec![send_byte; length];
+            socket.send_to(&send_buf, ("fe80::1", 4242))?;
+
+            let mut recv_buf = vec![0; length];
+            let actual = socket.recv(&mut recv_buf)?;
+
+            if actual == length && recv_buf.iter().all(|b| *b == receive_byte) {
+                println!("PASS");
+            }
+        }
+    }
+    Ok(())
+}