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(()) +}