Browse Source

Implement TAP interface support.

whitequark 8 years ago
parent
commit
f926fea49e
8 changed files with 268 additions and 108 deletions
  1. 5 3
      src/lib.rs
  2. 16 11
      src/phy/mod.rs
  3. 23 94
      src/phy/raw_socket.rs
  4. 11 0
      src/phy/sys/linux.rs
  5. 42 0
      src/phy/sys/mod.rs
  6. 74 0
      src/phy/sys/raw_socket.rs
  7. 55 0
      src/phy/sys/tap_interface.rs
  8. 42 0
      src/phy/tap_interface.rs

+ 5 - 3
src/lib.rs

@@ -1,11 +1,13 @@
 #![feature(range_contains, associated_consts)]
 #![no_std]
 
-#[cfg(test)]
+extern crate byteorder;
+
+#[cfg(any(test, feature = "std"))]
 #[macro_use]
 extern crate std;
-
-extern crate byteorder;
+#[cfg(feature = "std")]
+extern crate libc;
 
 pub mod phy;
 pub mod wire;

+ 16 - 11
src/phy/mod.rs

@@ -2,13 +2,21 @@
 //!
 //! The `phy` module provides an interface for sending and receiving frames
 //! through a physical (or perhaps virtualized) network device, [Device](trait.Device.html),
-//! as well as some useful implementations of that trait.
-//!
-//! Currently the only implementation, [RawSocket](struct.RawSocket.html), is based on
-//! Unix raw sockets, and only works on Linux.
+//! as well as an implementations of that trait that uses the host OS,
+//! [OsDevice](struct.OsDevice.html).
+
+#[cfg(feature = "std")]
+mod sys;
 
-#[cfg(all(unix, feature = "std"))]
+#[cfg(feature = "std")]
 mod raw_socket;
+#[cfg(all(feature = "std", target_os = "linux"))]
+mod tap_interface;
+
+#[cfg(feature = "std")]
+pub use self::raw_socket::RawSocket;
+#[cfg(all(feature = "std", target_os = "linux"))]
+pub use self::tap_interface::TapInterface;
 
 /// An interface for sending and receiving raw network frames.
 ///
@@ -32,13 +40,10 @@ pub trait Device {
     /// Transmits a frame.
     ///
     /// It is expected that a `send` implementation would gain ownership of a buffer with
-    /// the requested size, provide it for emission, and then schedule it to be read from
+    /// the requested length, provide it for emission, and then schedule it to be read from
     /// memory by the network device.
     ///
     /// # Panics
-    /// This function may panic if `size` is larger than `MTU`.
-    fn send<F: FnOnce(&mut [u8])>(&mut self, size: usize, handler: F);
+    /// This function may panic if `len` is larger than `MTU`.
+    fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F);
 }
-
-#[cfg(all(unix, feature = "std"))]
-pub use self::raw_socket::RawSocket;

+ 23 - 94
src/phy/raw_socket.rs

@@ -1,112 +1,41 @@
-extern crate std;
-extern crate libc;
+use std::{vec, io};
+use super::{sys, Device};
 
-use self::std::{mem, vec, io};
-
-#[repr(C)]
-struct ifreq {
-    ifr_name: [libc::c_char; libc::IF_NAMESIZE],
-    ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
-}
-
-const SIOCGIFMTU:   libc::c_ulong = 0x8921;
-const SIOCGIFINDEX: libc::c_ulong = 0x8933;
-
-const ETH_P_ALL:    libc::c_short = 0x0003;
-
-/// A raw socket: a socket that captures the entire packet, up to and including
-/// the link layer header.
+/// A socket that captures or transmits the complete frame.
 #[derive(Debug)]
 pub struct RawSocket {
-    sockfd: libc::c_int,
+    lower:  sys::RawSocketDesc,
     buffer: vec::Vec<u8>
 }
 
 impl RawSocket {
-    /// Creates and returns a raw socket, bound to the interface called `name`.
+    /// Creates a raw socket, bound to the interface called `name`.
+    ///
+    /// This requires superuser privileges or a corresponding capability bit
+    /// set on the executable.
     pub fn new(name: &str) -> io::Result<RawSocket> {
-        unsafe {
-            let sockfd = libc::socket(libc::AF_PACKET, libc::SOCK_RAW, ETH_P_ALL.to_be() as i32);
-            if sockfd == -1 {
-                return Err(io::Error::last_os_error())
-            }
-
-            let mut ifreq = ifreq {
-                ifr_name: [0; libc::IF_NAMESIZE],
-                ifr_data: 0
-            };
-            for (i, byte) in name.as_bytes().iter().enumerate() {
-                ifreq.ifr_name[i] = *byte as libc::c_char
-            }
-
-            let res = libc::ioctl(sockfd, SIOCGIFINDEX, &mut ifreq as *mut ifreq);
-            if res == -1 {
-                libc::close(sockfd);
-                return Err(io::Error::last_os_error())
-            }
-            let if_index = ifreq.ifr_data;
-
-            let res = libc::ioctl(sockfd, SIOCGIFMTU, &mut ifreq as *mut ifreq);
-            if res == -1 {
-                libc::close(sockfd);
-                return Err(io::Error::last_os_error())
-            }
-            let if_mtu = ifreq.ifr_data;
-
-            let sockaddr = libc::sockaddr_ll {
-                sll_family:   libc::AF_PACKET as u16,
-                sll_protocol: ETH_P_ALL.to_be() as u16,
-                sll_ifindex:  if_index as i32,
-                sll_hatype:   1,
-                sll_pkttype:  0,
-                sll_halen:    6,
-                sll_addr:     [0; 8]
-            };
-            libc::bind(sockfd,
-                       &sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
-                       mem::size_of::<libc::sockaddr_ll>() as u32);
-            if res == -1 {
-                libc::close(sockfd);
-                return Err(io::Error::last_os_error())
-            }
-
-            let mut buffer = vec::Vec::new();
-            buffer.resize(if_mtu as usize, 0);
-            Ok(RawSocket {
-                sockfd: sockfd,
-                buffer: buffer
-            })
-        }
+        let mut lower = try!(sys::RawSocketDesc::new(name));
+        try!(lower.bind_interface());
+
+        let mut buffer = vec::Vec::new();
+        buffer.resize(try!(lower.interface_mtu()), 0);
+        Ok(RawSocket {
+            lower:  lower,
+            buffer: buffer
+        })
     }
 }
 
-impl Drop for RawSocket {
-    fn drop(&mut self) {
-        unsafe { libc::close(self.sockfd); }
-    }
-}
-
-impl super::Device for RawSocket {
+impl Device for RawSocket {
     const MTU: usize = 1536;
 
     fn recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
-        let len = unsafe {
-            let len = libc::recv(self.sockfd, self.buffer.as_mut_ptr() as *mut libc::c_void,
-                                 self.buffer.len(), 0);
-            if len == -1 { Err(io::Error::last_os_error()).unwrap() }
-            len
-        };
-
-        handler(&self.buffer[..len as usize])
+        let len = self.lower.recv(&mut self.buffer[..]).unwrap();
+        handler(&self.buffer[..len])
     }
 
-    fn send<F: FnOnce(&mut [u8])>(&mut self, size: usize, handler: F) {
-        handler(&mut self.buffer[..size]);
-
-        unsafe {
-            let len = libc::send(self.sockfd, self.buffer.as_ptr() as *const libc::c_void,
-                                 size, 0);
-            if len == -1 { Err(io::Error::last_os_error()).unwrap() }
-        }
+    fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F) {
+        handler(&mut self.buffer[..len]);
+        self.lower.send(&self.buffer[..len]).unwrap();
     }
 }

+ 11 - 0
src/phy/sys/linux.rs

@@ -0,0 +1,11 @@
+use libc;
+
+pub const SIOCGIFMTU:   libc::c_ulong = 0x8921;
+pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
+
+pub const TUNSETIFF:    libc::c_ulong = 0x400454CA;
+
+pub const IFF_TAP:      libc::c_int   = 0x0002;
+pub const IFF_NO_PI:    libc::c_int   = 0x1000;
+
+pub const ETH_P_ALL:    libc::c_short = 0x0003;

+ 42 - 0
src/phy/sys/mod.rs

@@ -0,0 +1,42 @@
+use libc;
+use std::io;
+
+#[cfg(target_os = "linux")]
+#[path = "linux.rs"]
+mod imp;
+
+pub mod raw_socket;
+#[cfg(target_os = "linux")]
+pub mod tap_interface;
+
+pub use self::raw_socket::RawSocketDesc;
+#[cfg(target_os = "linux")]
+pub use self::tap_interface::TapInterfaceDesc;
+
+#[repr(C)]
+#[derive(Debug)]
+struct ifreq {
+    ifr_name: [libc::c_char; libc::IF_NAMESIZE],
+    ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
+}
+
+fn ifreq_for(name: &str) -> ifreq {
+    let mut ifreq = ifreq {
+        ifr_name: [0; libc::IF_NAMESIZE],
+        ifr_data: 0
+    };
+    for (i, byte) in name.as_bytes().iter().enumerate() {
+        ifreq.ifr_name[i] = *byte as libc::c_char
+    }
+    ifreq
+}
+
+fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
+               cmd: libc::c_ulong) -> io::Result<libc::c_int> {
+    unsafe {
+        let res = libc::ioctl(lower, cmd, ifreq as *mut ifreq);
+        if res == -1 { return Err(io::Error::last_os_error()) }
+    }
+
+    Ok(ifreq.ifr_data)
+}

+ 74 - 0
src/phy/sys/raw_socket.rs

@@ -0,0 +1,74 @@
+use libc;
+use std::{mem, io};
+use super::*;
+
+#[derive(Debug)]
+pub struct RawSocketDesc {
+    lower: libc::c_int,
+    ifreq: ifreq
+}
+
+impl RawSocketDesc {
+    pub fn new(name: &str) -> io::Result<RawSocketDesc> {
+        let lower = unsafe {
+            let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW,
+                                     imp::ETH_P_ALL.to_be() as i32);
+            if lower == -1 { return Err(io::Error::last_os_error()) }
+            lower
+        };
+
+        Ok(RawSocketDesc {
+            lower: lower,
+            ifreq: ifreq_for(name)
+        })
+    }
+
+    pub fn interface_mtu(&mut self) -> io::Result<usize> {
+        ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)
+    }
+
+    pub fn bind_interface(&mut self) -> io::Result<()> {
+        let sockaddr = libc::sockaddr_ll {
+            sll_family:   libc::AF_PACKET as u16,
+            sll_protocol: imp::ETH_P_ALL.to_be() as u16,
+            sll_ifindex:  try!(ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)),
+            sll_hatype:   1,
+            sll_pkttype:  0,
+            sll_halen:    6,
+            sll_addr:     [0; 8]
+        };
+
+        unsafe {
+            let res = libc::bind(self.lower,
+                                 &sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
+                                 mem::size_of::<libc::sockaddr_ll>() as u32);
+            if res == -1 { return Err(io::Error::last_os_error()) }
+        }
+
+        Ok(())
+    }
+
+    pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
+        unsafe {
+            let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
+                                 buffer.len(), 0);
+            if len == -1 { return Err(io::Error::last_os_error()) }
+            Ok(len as usize)
+        }
+    }
+
+    pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
+        unsafe {
+            let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void,
+                                 buffer.len(), 0);
+            if len == -1 { Err(io::Error::last_os_error()).unwrap() }
+            Ok(len as usize)
+        }
+    }
+}
+
+impl Drop for RawSocketDesc {
+    fn drop(&mut self) {
+        unsafe { libc::close(self.lower); }
+    }
+}

+ 55 - 0
src/phy/sys/tap_interface.rs

@@ -0,0 +1,55 @@
+use libc;
+use std::io;
+use super::*;
+
+#[cfg(target_os = "linux")]
+#[derive(Debug)]
+pub struct TapInterfaceDesc {
+    lower: libc::c_int,
+    ifreq: ifreq
+}
+
+impl TapInterfaceDesc {
+    pub fn new(name: &str) -> io::Result<TapInterfaceDesc> {
+        let lower = unsafe {
+            let lower = libc::open("/dev/net/tun".as_ptr() as *const libc::c_char,
+                                   libc::O_RDWR);
+            if lower == -1 { return Err(io::Error::last_os_error()) }
+            lower
+        };
+
+        Ok(TapInterfaceDesc {
+            lower: lower,
+            ifreq: ifreq_for(name)
+        })
+    }
+
+    pub fn attach_interface(&mut self) -> io::Result<()> {
+        self.ifreq.ifr_data = imp::IFF_TAP | imp::IFF_NO_PI;
+        ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
+    }
+
+    pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
+        unsafe {
+            let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
+                                 buffer.len());
+            if len == -1 { return Err(io::Error::last_os_error()) }
+            Ok(len as usize)
+        }
+    }
+
+    pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
+        unsafe {
+            let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void,
+                                  buffer.len());
+            if len == -1 { Err(io::Error::last_os_error()).unwrap() }
+            Ok(len as usize)
+        }
+    }
+}
+
+impl Drop for TapInterfaceDesc {
+    fn drop(&mut self) {
+        unsafe { libc::close(self.lower); }
+    }
+}

+ 42 - 0
src/phy/tap_interface.rs

@@ -0,0 +1,42 @@
+use std::{vec, io};
+use super::{sys, Device};
+
+/// A virtual Ethernet interface.
+#[derive(Debug)]
+pub struct TapInterface {
+    lower:  sys::TapInterfaceDesc,
+    buffer: vec::Vec<u8>
+}
+
+impl TapInterface {
+    /// Attaches to a TAP interface called `name`, or creates it if it does not exist.
+    ///
+    /// If `name` is a persistent interface configured with UID of the current user,
+    /// no special privileges are needed. Otherwise, this requires superuser privileges
+    /// or a corresponding capability set on the executable.
+    pub fn new(name: &str) -> io::Result<TapInterface> {
+        let mut lower = try!(sys::TapInterfaceDesc::new(name));
+        try!(lower.attach_interface());
+
+        let mut buffer = vec::Vec::new();
+        buffer.resize(Self::MTU, 0);
+        Ok(TapInterface {
+            lower:  lower,
+            buffer: buffer
+        })
+    }
+}
+
+impl Device for TapInterface {
+    const MTU: usize = 1536;
+
+    fn recv<F: FnOnce(&[u8])>(&mut self, handler: F) {
+        let len = self.lower.recv(&mut self.buffer[..]).unwrap();
+        handler(&self.buffer[..len])
+    }
+
+    fn send<F: FnOnce(&mut [u8])>(&mut self, len: usize, handler: F) {
+        handler(&mut self.buffer[..len]);
+        self.lower.send(&self.buffer[..len]).unwrap();
+    }
+}