Implement raw socket interface and tcpdump.
This commit is contained in:
parent
32ff49a795
commit
b1149e746c
|
@ -5,3 +5,8 @@ authors = ["whitequark <whitequark@whitequark.org>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = { version = "0.5", default-features = false }
|
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;
|
extern crate byteorder;
|
||||||
|
|
||||||
pub mod wire;
|
pub mod wire;
|
||||||
|
pub mod interface;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use core::fmt;
|
||||||
use byteorder::{ByteOrder, NetworkEndian};
|
use byteorder::{ByteOrder, NetworkEndian};
|
||||||
|
|
||||||
pub use super::EthernetProtocolType as ProtocolType;
|
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};
|
use super::{EthernetAddress, Ipv4Address};
|
||||||
|
|
||||||
/// A high-level representation of an Address Resolution Protocol packet.
|
/// A high-level representation of an Address Resolution Protocol packet.
|
||||||
|
@ -254,10 +273,8 @@ impl Repr {
|
||||||
match self {
|
match self {
|
||||||
&Repr::EthernetIpv4 {
|
&Repr::EthernetIpv4 {
|
||||||
operation,
|
operation,
|
||||||
source_hardware_addr,
|
source_hardware_addr, source_protocol_addr,
|
||||||
source_protocol_addr,
|
target_hardware_addr, target_protocol_addr
|
||||||
target_hardware_addr,
|
|
||||||
target_protocol_addr
|
|
||||||
} => {
|
} => {
|
||||||
packet.set_hardware_type(HardwareType::Ethernet);
|
packet.set_hardware_type(HardwareType::Ethernet);
|
||||||
packet.set_protocol_type(ProtocolType::Ipv4);
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -3,10 +3,21 @@ use byteorder::{ByteOrder, NetworkEndian};
|
||||||
|
|
||||||
enum_with_unknown! {
|
enum_with_unknown! {
|
||||||
/// Ethernet protocol type.
|
/// Ethernet protocol type.
|
||||||
pub enum ProtocolType(u16) {
|
pub enum EtherType(u16) {
|
||||||
Ipv4 = 0x0800,
|
Ipv4 = 0x0800,
|
||||||
Arp = 0x0806,
|
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 SOURCE: Field = 0..6;
|
||||||
pub const DESTINATION: Field = 6..12;
|
pub const DESTINATION: Field = 6..12;
|
||||||
pub const LENGTH: Field = 12..14;
|
pub const ETHERTYPE: Field = 12..14;
|
||||||
pub const PAYLOAD: FieldFrom = 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.
|
/// is too small or too large to contain one.
|
||||||
pub fn new(storage: T) -> Result<Frame<T>, ()> {
|
pub fn new(storage: T) -> Result<Frame<T>, ()> {
|
||||||
let len = storage.as_ref().len();
|
let len = storage.as_ref().len();
|
||||||
if !(64..1518).contains(len) {
|
if !(14..1518).contains(len) {
|
||||||
Err(()) // TODO: error type?
|
Err(()) // TODO: error type?
|
||||||
} else {
|
} else {
|
||||||
Ok(Frame(storage))
|
Ok(Frame(storage))
|
||||||
|
@ -83,11 +94,12 @@ impl<T: AsRef<[u8]>> Frame<T> {
|
||||||
Address::from_bytes(&bytes[field::DESTINATION])
|
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)]
|
#[inline(always)]
|
||||||
pub fn length(&self) -> u16 {
|
pub fn ethertype(&self) -> EtherType {
|
||||||
let bytes = self.0.as_ref();
|
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.
|
/// 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())
|
bytes[field::DESTINATION].copy_from_slice(value.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the length field.
|
/// Set the EtherType field.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_length(&mut self, value: u16) {
|
pub fn set_ethertype(&mut self, value: EtherType) {
|
||||||
let bytes = self.0.as_mut();
|
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.
|
/// 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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -24,7 +24,6 @@ impl Address {
|
||||||
impl fmt::Display for Address {
|
impl fmt::Display for Address {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let bytes = self.0;
|
let bytes = self.0;
|
||||||
write!(f, "{:02x}.{:02x}.{:02x}.{:02x}",
|
write!(f, "{}.{}.{}.{}", bytes[0], bytes[1], bytes[2], bytes[3])
|
||||||
bytes[0], bytes[1], bytes[2], bytes[3])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ mod ethernet;
|
||||||
mod arp;
|
mod arp;
|
||||||
mod ipv4;
|
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::Address as EthernetAddress;
|
||||||
pub use self::ethernet::Frame as EthernetFrame;
|
pub use self::ethernet::Frame as EthernetFrame;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue