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: 68d9c86a5b15
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: 10d538c02fa3
Choose a head ref
  • 2 commits
  • 9 files changed
  • 1 contributor

Commits on Dec 10, 2016

  1. Copy the full SHA
    34d5e1b View commit details
  2. Copy the full SHA
    10d538c View commit details
Showing with 315 additions and 81 deletions.
  1. +5 −0 Cargo.toml
  2. +39 −0 examples/smoltcpdump.rs
  3. +10 −0 src/interface/mod.rs
  4. +101 −0 src/interface/raw_socket.rs
  5. +2 −50 src/lib.rs
  6. +55 −18 src/{ → wire}/arp.rs
  7. +30 −11 src/{ → wire}/ethernet.rs
  8. +1 −2 src/{ → wire}/ipv4.rs
  9. +72 −0 src/wire/mod.rs
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -5,3 +5,8 @@ authors = ["whitequark <whitequark@whitequark.org>"]

[dependencies]
byteorder = { version = "0.5", default-features = false }
libc = { version = "0.2.18", optional = true }

[features]
std = ["libc"]
default = ["std"]
39 changes: 39 additions & 0 deletions examples/smoltcpdump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
extern crate smoltcp;

use std::{env, io};
use smoltcp::wire::{EthernetFrame, EthernetProtocolType, ArpPacket};
use smoltcp::interface::RawSocket;

fn get<T>(result: Result<T, ()>) -> io::Result<T> {
result.map_err(|()| io::Error::new(io::ErrorKind::InvalidData,
"buffer too small"))
.into()
}

fn print_frame(socket: &mut RawSocket) -> io::Result<()> {
let buffer = try!(socket.capture());

let frame = try!(get(EthernetFrame::new(&buffer[..])));
println!("{}", frame);

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

Ok(())
}

fn main() {
let ifname = env::args().nth(1).unwrap();
let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
loop {
match print_frame(&mut socket) {
Ok(()) => (),
Err(e) => println!("Cannot print frame: {}", e)
}
}
}
10 changes: 10 additions & 0 deletions src/interface/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//! Access to networking hardware.
//!
//! The `interface` module provides a way to capture and inject packets.
//! It requires the standard library, and currently only works on Linux.
#[cfg(all(unix, feature = "std"))]
mod raw_socket;

#[cfg(all(unix, feature = "std"))]
pub use self::raw_socket::RawSocket;
101 changes: 101 additions & 0 deletions src/interface/raw_socket.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
extern crate std;
extern crate libc;

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.
#[derive(Debug)]
pub struct RawSocket {
sockfd: libc::c_int,
buffer: vec::Vec<u8>
}

impl RawSocket {
/// Creates and returns a raw socket, bound to the interface called `name`.
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
})
}
}

/// Captures a packet into the internal buffer, which is sized appropriately
/// for the interface MTU.
pub fn capture(&mut self) -> io::Result<&[u8]> {
unsafe {
let len = libc::recv(self.sockfd, self.buffer.as_mut_ptr() as *mut libc::c_void,
self.buffer.len(), 0);
if len == -1 {
return Err(io::Error::last_os_error())
}

Ok(&self.buffer[..len as usize])
}
}
}

impl Drop for RawSocket {
fn drop(&mut self) {
unsafe { libc::close(self.sockfd); }
}
}
52 changes: 2 additions & 50 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -7,53 +7,5 @@ extern crate std;

extern crate byteorder;

macro_rules! enum_with_unknown {
(#[$( $attr:meta ),*]
pub enum $name:ident($ty:ty) { $( $variant:ident = $value:expr ),+ }) => {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[$( $attr ),*]
pub enum $name {
$( $variant ),*,
Unknown($ty)
}

impl ::core::convert::From<$ty> for $name {
fn from(value: $ty) -> Self {
match value {
$( $value => $name::$variant ),*,
other => $name::Unknown(other)
}
}
}

impl ::core::convert::From<$name> for $ty {
fn from(value: $name) -> Self {
match value {
$( $name::$variant => $value ),*,
$name::Unknown(other) => other
}
}
}
}
}

mod field {
pub type Field = ::core::ops::Range<usize>;
pub type FieldFrom = ::core::ops::RangeFrom<usize>;
}

mod ethernet;
mod arp;
mod ipv4;

pub use ethernet::ProtocolType as EthernetProtocolType;
pub use ethernet::Address as EthernetAddress;
pub use ethernet::Frame as EthernetFrame;

pub use arp::HardwareType as ArpHardwareType;
pub use arp::ProtocolType as ArpProtocolType;
pub use arp::Operation as ArpOperation;
pub use arp::Packet as ArpPacket;
pub use arp::Repr as ArpRepr;

pub use ipv4::Address as Ipv4Address;
pub mod wire;
pub mod interface;
73 changes: 55 additions & 18 deletions src/arp.rs → src/wire/arp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};

pub use ::ethernet::ProtocolType as ProtocolType;
pub use super::EthernetProtocolType as ProtocolType;

enum_with_unknown! {
/// ARP network protocol type.
@@ -24,7 +25,7 @@ pub struct Packet<T: AsRef<[u8]>>(T);
mod field {
#![allow(non_snake_case)]

use ::field::*;
use ::wire::field::*;

pub const HTYPE: Field = 0..2;
pub const PTYPE: Field = 2..4;
@@ -209,16 +210,36 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
}
}

impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match Repr::parse(self) {
Ok(repr) => write!(f, "{}", repr),
_ => {
try!(write!(f, "ARP htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(), self.protocol_type(),
self.hardware_length(), self.protocol_length(),
self.operation()));
try!(write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(), self.source_protocol_addr(),
self.target_hardware_addr(), self.target_protocol_addr()));
Ok(())
}
}
}
}

use super::{EthernetAddress, Ipv4Address};

/// A high-level representation of an Address Resolution Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Repr {
/// An Ethernet and IPv4 Address Resolution Protocol packet.
EthernetIpv4 {
operation: Operation,
source_hardware_addr: ::EthernetAddress,
source_protocol_addr: ::Ipv4Address,
target_hardware_addr: ::EthernetAddress,
target_protocol_addr: ::Ipv4Address
source_hardware_addr: EthernetAddress,
source_protocol_addr: Ipv4Address,
target_hardware_addr: EthernetAddress,
target_protocol_addr: Ipv4Address
},
#[doc(hidden)]
__Nonexhaustive
@@ -234,13 +255,13 @@ impl Repr {
Ok(Repr::EthernetIpv4 {
operation: packet.operation(),
source_hardware_addr:
::EthernetAddress::from_bytes(packet.source_hardware_addr()),
EthernetAddress::from_bytes(packet.source_hardware_addr()),
source_protocol_addr:
::Ipv4Address::from_bytes(packet.source_protocol_addr()),
Ipv4Address::from_bytes(packet.source_protocol_addr()),
target_hardware_addr:
::EthernetAddress::from_bytes(packet.target_hardware_addr()),
EthernetAddress::from_bytes(packet.target_hardware_addr()),
target_protocol_addr:
::Ipv4Address::from_bytes(packet.target_protocol_addr())
Ipv4Address::from_bytes(packet.target_protocol_addr())
})
},
_ => Err(())
@@ -252,10 +273,8 @@ impl Repr {
match self {
&Repr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr
} => {
packet.set_hardware_type(HardwareType::Ethernet);
packet.set_protocol_type(ProtocolType::Ipv4);
@@ -272,6 +291,24 @@ impl Repr {
}
}

impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&Repr::EthernetIpv4 {
operation,
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr
} => {
write!(f, "ARP type=Ethernet+IPv4 src={}/{} dst={}/{} op={:?}",
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr,
operation)
},
&Repr::__Nonexhaustive => unreachable!()
}
}
}

#[cfg(test)]
mod test {
use super::*;
@@ -321,13 +358,13 @@ mod test {
Repr::EthernetIpv4 {
operation: Operation::Request,
source_hardware_addr:
::EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]),
EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]),
source_protocol_addr:
::Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
target_hardware_addr:
::EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]),
EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]),
target_protocol_addr:
::Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44])
Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44])
}
}

Loading