diff options
author | Stephen Barber <smbarber@chromium.org> | 2017-06-15 23:21:15 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-07-13 22:03:30 -0700 |
commit | 253ac89d919b6d926c770e90a604ba0439ef646b (patch) | |
tree | ed5e607201cedf7ce2387fa012b9b26c970949b9 /net_util | |
parent | 219b1856eb41c8c6a0dfefb4d8d257afe70886f9 (diff) | |
download | crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.tar crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.tar.gz crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.tar.bz2 crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.tar.lz crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.tar.xz crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.tar.zst crosvm-253ac89d919b6d926c770e90a604ba0439ef646b.zip |
net_util: add crate for creating/configuring tap interfaces
Signed-off-by: Stephen Barber <smbarber@chromium.org> BUG=chromium:738639 TEST=cargo test Change-Id: Iddf715d40164abeeb6923e8e5a84c02233e0ab64 Reviewed-on: https://chromium-review.googlesource.com/538103 Commit-Ready: Stephen Barber <smbarber@chromium.org> Tested-by: Stephen Barber <smbarber@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'net_util')
-rw-r--r-- | net_util/Cargo.toml | 9 | ||||
-rw-r--r-- | net_util/src/lib.rs | 246 |
2 files changed, 255 insertions, 0 deletions
diff --git a/net_util/Cargo.toml b/net_util/Cargo.toml new file mode 100644 index 0000000..1bcf18c --- /dev/null +++ b/net_util/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "net_util" +version = "0.1.0" +authors = ["The Chromium OS Authors"] + +[dependencies] +libc = "*" +net_sys = { path = "../net_sys" } +sys_util = { path = "../sys_util" } diff --git a/net_util/src/lib.rs b/net_util/src/lib.rs new file mode 100644 index 0000000..5ef4cd9 --- /dev/null +++ b/net_util/src/lib.rs @@ -0,0 +1,246 @@ +// Copyright 2017 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +extern crate libc; +extern crate net_sys; +extern crate sys_util; + +use std::fs::File; +use std::mem; +use std::net; +use std::os::raw::*; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; + +use sys_util::{ioctl_with_val, ioctl_with_ref, ioctl_with_mut_ref}; + +#[derive(Debug)] +pub enum Error { + /// Failed to create a socket. + CreateSocket(sys_util::Error), + /// Couldn't open /dev/net/tun. + OpenTun(sys_util::Error), + /// Unable to create tap interface. + CreateTap(sys_util::Error), + /// ioctl failed. + IoctlError(sys_util::Error), +} +pub type Result<T> = std::result::Result<T, Error>; + +/// Create a sockaddr_in from an IPv4 address, and expose it as +/// an opaque sockaddr suitable for usage by socket ioctls. +fn create_sockaddr(ip_addr: net::Ipv4Addr) -> net_sys::sockaddr { + // IPv4 addresses big-endian (network order), but Ipv4Addr will give us + // a view of those bytes directly so we can avoid any endian trickiness. + let addr_in = net_sys::sockaddr_in { + sin_family: net_sys::AF_INET as u16, + sin_port: 0, + sin_addr: unsafe { mem::transmute(ip_addr.octets()) }, + __pad: [0; 8usize], + }; + + unsafe { mem::transmute(addr_in) } +} + +fn create_socket() -> Result<net::UdpSocket> { + // This is safe since we check the return value. + let sock = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) }; + if sock < 0 { + return Err(Error::CreateSocket(sys_util::Error::last())); + } + + // This is safe; nothing else will use or hold onto the raw sock fd. + Ok(unsafe { net::UdpSocket::from_raw_fd(sock) }) +} + +/// Handle for a network tap interface. +/// +/// For now, this simply wraps the file descriptor for the tap device so methods +/// can run ioctls on the interface. The tap interface fd will be closed when +/// Tap goes out of scope, and the kernel will clean up the interface +/// automatically. +pub struct Tap { + tap_file: File, + if_name: [u8; 16usize], +} + +impl Tap { + /// Create a new tap interface. + pub fn new() -> Result<Tap> { + // Open calls are safe because we give a constant nul-terminated + // string and verify the result. + let fd = unsafe { + libc::open(b"/dev/net/tun\0".as_ptr() as *const i8, + libc::O_RDWR | libc::O_NONBLOCK | libc::O_CLOEXEC) + }; + if fd < 0 { + return Err(Error::OpenTun(sys_util::Error::last())); + } + + // We just checked that the fd is valid. + let tuntap = unsafe { File::from_raw_fd(fd) }; + + const TUNTAP_DEV_FORMAT: &'static [u8; 8usize] = b"vmtap%d\0"; + + // This is pretty messy because of the unions used by ifreq. Since we + // don't call as_mut on the same union field more than once, this block + // is safe. + let mut ifreq: net_sys::ifreq = Default::default(); + unsafe { + let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); + let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut(); + let mut name_slice = &mut ifrn_name[..TUNTAP_DEV_FORMAT.len()]; + name_slice.copy_from_slice(TUNTAP_DEV_FORMAT); + *ifru_flags = (net_sys::IFF_TAP | + net_sys::IFF_NO_PI | + net_sys::IFF_VNET_HDR) as c_short; + } + + // ioctl is safe since we call it with a valid tap fd and check the return + // value. + let ret = unsafe { ioctl_with_mut_ref(&tuntap, net_sys::TUNSETIFF(), &mut ifreq) }; + if ret < 0 { + return Err(Error::CreateTap(sys_util::Error::last())); + } + + // Safe since only the name is accessed, and it's cloned out. + Ok(Tap { + tap_file: tuntap, + if_name: unsafe { ifreq.ifr_ifrn.ifrn_name.as_ref().clone() }, + }) + } + + /// Set the host-side IP address for the tap interface. + pub fn set_ip_addr(&self, ip_addr: net::Ipv4Addr) -> Result<()> { + let sock = create_socket()?; + let addr = create_sockaddr(ip_addr); + + let mut ifreq = self.get_ifreq(); + + // We only access one field of the ifru union, hence this is safe. + unsafe { + let ifru_addr = ifreq.ifr_ifru.ifru_addr.as_mut(); + *ifru_addr = addr; + } + + // ioctl is safe. Called with a valid sock fd, and we check the return. + let ret = unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFADDR as u64, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(sys_util::Error::last())); + } + + Ok(()) + } + + /// Set the netmask for the subnet that the tap interface will exist on. + pub fn set_netmask(&self, netmask: net::Ipv4Addr) -> Result<()> { + let sock = create_socket()?; + let addr = create_sockaddr(netmask); + + let mut ifreq = self.get_ifreq(); + + // We only access one field of the ifru union, hence this is safe. + unsafe { + let ifru_addr = ifreq.ifr_ifru.ifru_addr.as_mut(); + *ifru_addr = addr; + } + + // ioctl is safe. Called with a valid sock fd, and we check the return. + let ret = unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFNETMASK as u64, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(sys_util::Error::last())); + } + + Ok(()) + } + + /// Set the offload flags for the tap interface. + pub fn set_offload(&self, flags: c_uint) -> Result<()> { + // ioctl is safe. Called with a valid tap fd, and we check the return. + let ret = unsafe { ioctl_with_val(&self.tap_file, net_sys::TUNSETOFFLOAD(), flags as u64) }; + if ret < 0 { + return Err(Error::IoctlError(sys_util::Error::last())); + } + + Ok(()) + } + + /// Enable the tap interface. + pub fn enable(&self) -> Result<()> { + let sock = create_socket()?; + + let mut ifreq = self.get_ifreq(); + + // We only access one field of the ifru union, hence this is safe. + unsafe { + let ifru_flags = ifreq.ifr_ifru.ifru_flags.as_mut(); + *ifru_flags = (net_sys::net_device_flags_IFF_UP | + net_sys::net_device_flags_IFF_RUNNING) as i16; + } + + // ioctl is safe. Called with a valid sock fd, and we check the return. + let ret = unsafe { ioctl_with_ref(&sock, net_sys::sockios::SIOCSIFFLAGS as u64, &ifreq) }; + if ret < 0 { + return Err(Error::IoctlError(sys_util::Error::last())); + } + + Ok(()) + } + + /// Set the size of the vnet hdr. + pub fn set_vnet_hdr_size(&self, size: c_int) -> Result<()> { + // ioctl is safe. Called with a valid tap fd, and we check the return. + let ret = unsafe { ioctl_with_ref(&self.tap_file, net_sys::TUNSETVNETHDRSZ(), &size) }; + if ret < 0 { + return Err(Error::IoctlError(sys_util::Error::last())); + } + + Ok(()) + } + + fn get_ifreq(&self) -> net_sys::ifreq { + let mut ifreq: net_sys::ifreq = Default::default(); + + // This sets the name of the interface, which is the only entry + // in a single-field union. + unsafe { + let ifrn_name = ifreq.ifr_ifrn.ifrn_name.as_mut(); + ifrn_name.clone_from_slice(&self.if_name); + } + + ifreq + } +} + +impl AsRawFd for Tap { + fn as_raw_fd(&self) -> RawFd { + self.tap_file.as_raw_fd() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn tap_create() { + Tap::new().unwrap(); + } + + #[test] + fn tap_configure() { + let tap = Tap::new().unwrap(); + let ip_addr: net::Ipv4Addr = "100.115.92.5".parse().unwrap(); + let netmask: net::Ipv4Addr = "255.255.255.252".parse().unwrap(); + + tap.set_ip_addr(ip_addr).unwrap(); + tap.set_netmask(netmask).unwrap(); + } + + #[test] + fn tap_enable() { + let tap = Tap::new().unwrap(); + + tap.enable().unwrap(); + } +} |