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