Implement raw socket interface and tcpdump.
parent
32ff49a795
commit
b1149e746c
|
@ -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"]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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); }
|
||||
}
|
||||
}
|
|
@ -8,3 +8,4 @@ extern crate std;
|
|||
extern crate byteorder;
|
||||
|
||||
pub mod wire;
|
||||
pub mod interface;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use core::fmt;
|
||||
use byteorder::{ByteOrder, NetworkEndian};
|
||||
|
||||
pub use super::EthernetProtocolType as ProtocolType;
|
||||
|
@ -209,6 +210,24 @@ 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.
|
||||
|
@ -254,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);
|
||||
|
@ -274,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::*;
|
||||
|
|
|
@ -3,10 +3,21 @@ use byteorder::{ByteOrder, NetworkEndian};
|
|||
|
||||
enum_with_unknown! {
|
||||
/// Ethernet protocol type.
|
||||
pub enum ProtocolType(u16) {
|
||||
pub enum EtherType(u16) {
|
||||
Ipv4 = 0x0800,
|
||||
Arp = 0x0806,
|
||||
Iv6 = 0x86DD
|
||||
Ipv6 = 0x86DD
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EtherType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
&EtherType::Ipv4 => write!(f, "IPv4"),
|
||||
&EtherType::Ipv6 => write!(f, "IPv6"),
|
||||
&EtherType::Arp => write!(f, "ARP"),
|
||||
&EtherType::Unknown(ty) => write!(f, "0x{:04x}", ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +59,7 @@ mod field {
|
|||
|
||||
pub const SOURCE: Field = 0..6;
|
||||
pub const DESTINATION: Field = 6..12;
|
||||
pub const LENGTH: Field = 12..14;
|
||||
pub const ETHERTYPE: Field = 12..14;
|
||||
pub const PAYLOAD: FieldFrom = 14..;
|
||||
}
|
||||
|
||||
|
@ -57,7 +68,7 @@ impl<T: AsRef<[u8]>> Frame<T> {
|
|||
/// is too small or too large to contain one.
|
||||
pub fn new(storage: T) -> Result<Frame<T>, ()> {
|
||||
let len = storage.as_ref().len();
|
||||
if !(64..1518).contains(len) {
|
||||
if !(14..1518).contains(len) {
|
||||
Err(()) // TODO: error type?
|
||||
} else {
|
||||
Ok(Frame(storage))
|
||||
|
@ -83,11 +94,12 @@ impl<T: AsRef<[u8]>> Frame<T> {
|
|||
Address::from_bytes(&bytes[field::DESTINATION])
|
||||
}
|
||||
|
||||
/// Return the length field, without checking for 802.1Q.
|
||||
/// Return the EtherType field, without checking for 802.1Q.
|
||||
#[inline(always)]
|
||||
pub fn length(&self) -> u16 {
|
||||
pub fn ethertype(&self) -> EtherType {
|
||||
let bytes = self.0.as_ref();
|
||||
NetworkEndian::read_u16(&bytes[field::LENGTH])
|
||||
let raw = NetworkEndian::read_u16(&bytes[field::ETHERTYPE]);
|
||||
EtherType::from(raw)
|
||||
}
|
||||
|
||||
/// Return a pointer to the payload, without checking for 802.1Q.
|
||||
|
@ -113,11 +125,11 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
|
|||
bytes[field::DESTINATION].copy_from_slice(value.as_bytes())
|
||||
}
|
||||
|
||||
/// Set the length field.
|
||||
/// Set the EtherType field.
|
||||
#[inline(always)]
|
||||
pub fn set_length(&mut self, value: u16) {
|
||||
pub fn set_ethertype(&mut self, value: EtherType) {
|
||||
let bytes = self.0.as_mut();
|
||||
NetworkEndian::write_u16(&mut bytes[field::LENGTH], value)
|
||||
NetworkEndian::write_u16(&mut bytes[field::ETHERTYPE], value.into())
|
||||
}
|
||||
|
||||
/// Return a mutable pointer to the payload.
|
||||
|
@ -128,6 +140,13 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Frame<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "EthernetII src={} dst={} type={}",
|
||||
self.source(), self.destination(), self.ethertype())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -24,7 +24,6 @@ impl Address {
|
|||
impl fmt::Display for Address {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.0;
|
||||
write!(f, "{:02x}.{:02x}.{:02x}.{:02x}",
|
||||
bytes[0], bytes[1], bytes[2], bytes[3])
|
||||
write!(f, "{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ mod ethernet;
|
|||
mod arp;
|
||||
mod ipv4;
|
||||
|
||||
pub use self::ethernet::ProtocolType as EthernetProtocolType;
|
||||
pub use self::ethernet::EtherType as EthernetProtocolType;
|
||||
pub use self::ethernet::Address as EthernetAddress;
|
||||
pub use self::ethernet::Frame as EthernetFrame;
|
||||
|
||||
|
|
Loading…
Reference in New Issue