Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 6a8989381f7e
Choose a base ref
...
head repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: f57492a67b30
Choose a head ref
  • 4 commits
  • 13 files changed
  • 1 contributor

Commits on Dec 11, 2016

  1. Fix tests.

    whitequark committed Dec 11, 2016
    Copy the full SHA
    c076f91 View commit details
  2. Copy the full SHA
    f926fea View commit details
  3. Copy the full SHA
    5eff804 View commit details
  4. Naming consistency.

    whitequark committed Dec 11, 2016
    Copy the full SHA
    f57492a View commit details
Showing with 446 additions and 189 deletions.
  1. +2 −20 examples/smoltcpdump.rs
  2. +5 −3 src/lib.rs
  3. +16 −11 src/phy/mod.rs
  4. +23 −94 src/phy/raw_socket.rs
  5. +11 −0 src/phy/sys/linux.rs
  6. +42 −0 src/phy/sys/mod.rs
  7. +74 −0 src/phy/sys/raw_socket.rs
  8. +55 −0 src/phy/sys/tap_interface.rs
  9. +42 −0 src/phy/tap_interface.rs
  10. +70 −58 src/wire/arp.rs
  11. +23 −3 src/wire/ethernet.rs
  12. +4 −0 src/wire/mod.rs
  13. +79 −0 src/wire/pretty_print.rs
22 changes: 2 additions & 20 deletions examples/smoltcpdump.rs
Original file line number Diff line number Diff line change
@@ -2,32 +2,14 @@ extern crate smoltcp;

use std::env;
use smoltcp::phy::{Device, RawSocket};
use smoltcp::wire::{EthernetFrame, EthernetProtocolType, ArpPacket};

fn print_frame(buffer: &[u8]) -> Result<(), ()> {
let frame = try!(EthernetFrame::new(&buffer[..]));
println!("{}", frame);

match frame.ethertype() {
EthernetProtocolType::Arp => {
let packet = try!(ArpPacket::new(frame.payload()));
println!("| {}", packet);
},
_ => ()
}

Ok(())
}
use smoltcp::wire::{PrettyPrinter, EthernetFrame};

fn main() {
let ifname = env::args().nth(1).unwrap();
let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
loop {
socket.recv(|buffer| {
match print_frame(buffer) {
Ok(()) => (),
Err(()) => println!("buffer too small")
}
print!("{}", PrettyPrinter::<EthernetFrame<_>>::new("", &buffer))
})
}
}
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
27 changes: 16 additions & 11 deletions src/phy/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
117 changes: 23 additions & 94 deletions src/phy/raw_socket.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions src/phy/sys/linux.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 42 additions & 0 deletions src/phy/sys/mod.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 74 additions & 0 deletions src/phy/sys/raw_socket.rs
Original file line number Diff line number Diff line change
@@ -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); }
}
}
Loading