summary refs log tree commit diff
path: root/net_util
diff options
context:
space:
mode:
authorStephen Barber <smbarber@chromium.org>2017-06-15 23:21:15 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-07-13 22:03:30 -0700
commit253ac89d919b6d926c770e90a604ba0439ef646b (patch)
treeed5e607201cedf7ce2387fa012b9b26c970949b9 /net_util
parent219b1856eb41c8c6a0dfefb4d8d257afe70886f9 (diff)
downloadcrosvm-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.toml9
-rw-r--r--net_util/src/lib.rs246
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();
+    }
+}