blob: 60945b142a60c7717161ff199017a9ccddd4be75 [file] [log] [blame]
// 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(())
}