parent
3afc519414
commit
b3172f19e1
@ -0,0 +1,111 @@ |
||||
#![allow(non_camel_case_types)] |
||||
#![allow(non_snake_case)] |
||||
#![allow(dead_code)] |
||||
|
||||
extern crate libc; |
||||
|
||||
pub type SockAddr = libc::sockaddr; |
||||
|
||||
pub const AF_LINK: libc::c_int = 18; |
||||
|
||||
const IF_NAMESIZE: usize = 16; |
||||
const IFNAMSIZ: usize = IF_NAMESIZE; |
||||
const IOC_IN: libc::c_ulong = 0x80000000; |
||||
const IOC_OUT: libc::c_ulong = 0x40000000; |
||||
const IOC_INOUT: libc::c_ulong = IOC_IN | IOC_OUT; |
||||
const IOCPARM_SHIFT: libc::c_ulong = 13; |
||||
const IOCPARM_MASK: libc::c_ulong = (1 << (IOCPARM_SHIFT as usize)) - 1; |
||||
|
||||
const SIZEOF_TIMEVAL: libc::c_ulong = 16; |
||||
const SIZEOF_IFREQ: libc::c_ulong = 32; |
||||
const SIZEOF_C_UINT: libc::c_ulong = 4; |
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] |
||||
const SIZEOF_C_LONG: libc::c_int = 8; |
||||
|
||||
pub const BIOCSETIF: libc::c_ulong = |
||||
IOC_IN | ((SIZEOF_IFREQ & IOCPARM_MASK) << 16usize) | (('B' as libc::c_ulong) << 8usize) | 108; |
||||
pub const BIOCIMMEDIATE: libc::c_ulong = |
||||
IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 112; |
||||
pub const BIOCGBLEN: libc::c_ulong = |
||||
IOC_OUT | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 102; |
||||
pub const BIOCGDLT: libc::c_ulong = |
||||
IOC_OUT | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 106; |
||||
|
||||
pub const BIOCSBLEN: libc::c_ulong = |
||||
IOC_INOUT | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 102; |
||||
pub const BIOCSHDRCMPLT: libc::c_ulong = |
||||
IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 117; |
||||
pub const BIOCSRTIMEOUT: libc::c_ulong = |
||||
IOC_IN | ((SIZEOF_TIMEVAL & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 109; |
||||
|
||||
#[cfg(target_os = "freebsd")] |
||||
pub const BIOCFEEDBACK: libc::c_ulong = |
||||
IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 124; |
||||
#[cfg(target_os = "netbsd")] |
||||
pub const BIOCFEEDBACK: libc::c_ulong = |
||||
IOC_IN | ((SIZEOF_C_UINT & IOCPARM_MASK) << 16) | (('B' as libc::c_ulong) << 8) | 125; |
||||
|
||||
pub const DLT_NULL: libc::c_uint = 0; |
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] |
||||
const BPF_ALIGNMENT: libc::c_int = SIZEOF_C_LONG; |
||||
#[cfg(any(target_os = "openbsd", target_os = "macos", target_os = "ios", windows))] |
||||
const BPF_ALIGNMENT: libc::c_int = 4; |
||||
|
||||
pub fn BPF_WORDALIGN(x: isize) -> isize { |
||||
let bpf_alignment = BPF_ALIGNMENT as isize; |
||||
(x + (bpf_alignment - 1)) & !(bpf_alignment - 1) |
||||
} |
||||
|
||||
pub struct ifreq { |
||||
pub ifr_name: [libc::c_char; IFNAMSIZ], |
||||
pub ifru_addr: SockAddr,
|
||||
} |
||||
|
||||
#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "macos", target_os = "ios"))] |
||||
pub struct sockaddr_dl { |
||||
pub sdl_len: libc::c_uchar, |
||||
pub sdl_family: libc::c_uchar, |
||||
pub sdl_index: libc::c_ushort, |
||||
pub sdl_type: libc::c_uchar, |
||||
pub sdl_nlen: libc::c_uchar, |
||||
pub sdl_alen: libc::c_uchar, |
||||
pub sdl_slen: libc::c_uchar, |
||||
pub sdl_data: [libc::c_char; 46], |
||||
} |
||||
|
||||
#[cfg(any(
|
||||
target_os = "freebsd", |
||||
target_os = "netbsd", |
||||
all(any(target_os = "macos", target_os = "ios"), target_pointer_width = "32"), |
||||
windows |
||||
))] |
||||
#[repr(C)] |
||||
pub struct bpf_hdr { |
||||
pub bh_tstamp: libc::timeval, |
||||
pub bh_caplen: u32, |
||||
pub bh_datalen: u32, |
||||
pub bh_hdrlen: libc::c_ushort, |
||||
} |
||||
|
||||
pub struct timeval32 { |
||||
pub tv_sec: i32, |
||||
pub tv_usec: i32, |
||||
} |
||||
|
||||
#[cfg(any(
|
||||
target_os = "openbsd", |
||||
all(any(target_os = "macos", target_os = "ios"), target_pointer_width = "64") |
||||
))] |
||||
#[repr(C)] |
||||
pub struct bpf_hdr { |
||||
pub bh_tstamp: timeval32, |
||||
pub bh_caplen: u32, |
||||
pub bh_datalen: u32, |
||||
pub bh_hdrlen: libc::c_ushort, |
||||
} |
||||
|
||||
#[cfg(not(windows))] |
||||
extern "C" { |
||||
pub fn ioctl(d: libc::c_int, request: libc::c_ulong, ...) -> libc::c_int; |
||||
} |
@ -1,4 +1,10 @@ |
||||
#[cfg(not(target_os="windows"))] |
||||
mod binding; |
||||
#[cfg(not(target_os="windows"))] |
||||
pub use self::binding::*; |
||||
|
||||
#[cfg(not(target_os="windows"))] |
||||
mod unix; |
||||
#[cfg(not(target_os="windows"))] |
||||
pub use self::unix::*; |
||||
|
||||
|
@ -1,17 +1,401 @@ |
||||
#![allow(non_camel_case_types)] |
||||
#![allow(non_snake_case)] |
||||
#![allow(dead_code)] |
||||
|
||||
pub const AF_LINK: libc::c_int = 18; |
||||
|
||||
#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "macos", target_os = "ios"))] |
||||
pub struct sockaddr_dl { |
||||
pub sdl_len: libc::c_uchar, |
||||
pub sdl_family: libc::c_uchar, |
||||
pub sdl_index: libc::c_ushort, |
||||
pub sdl_type: libc::c_uchar, |
||||
pub sdl_nlen: libc::c_uchar, |
||||
pub sdl_alen: libc::c_uchar, |
||||
pub sdl_slen: libc::c_uchar, |
||||
pub sdl_data: [libc::c_char; 46], |
||||
use super::binding; |
||||
use crate::socket::{DataLinkReceiver, DataLinkSender}; |
||||
use crate::interface::Interface; |
||||
|
||||
use std::collections::VecDeque; |
||||
use std::ffi::CString; |
||||
use std::io; |
||||
use std::mem; |
||||
use std::ptr; |
||||
use std::sync::Arc; |
||||
use std::time::Duration; |
||||
|
||||
static ETHERNET_HEADER_SIZE: usize = 14; |
||||
|
||||
pub type CSocket = libc::c_int; |
||||
pub type TvUsecType = libc::c_int; |
||||
|
||||
unsafe fn close(sock: CSocket) { |
||||
let _ = libc::close(sock); |
||||
} |
||||
|
||||
fn duration_to_timespec(dur: Duration) -> libc::timespec { |
||||
libc::timespec { |
||||
tv_sec: dur.as_secs() as libc::time_t, |
||||
tv_nsec: (dur.subsec_nanos() as TvUsecType).into(), |
||||
} |
||||
} |
||||
|
||||
pub struct FileDesc { |
||||
pub fd: CSocket, |
||||
} |
||||
|
||||
impl Drop for FileDesc { |
||||
fn drop(&mut self) { |
||||
unsafe { |
||||
close(self.fd); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
||||
pub struct Config { |
||||
pub write_buffer_size: usize, |
||||
pub read_buffer_size: usize, |
||||
pub read_timeout: Option<Duration>, |
||||
pub write_timeout: Option<Duration>, |
||||
pub bpf_fd_attempts: usize, |
||||
} |
||||
|
||||
impl<'a> From<&'a crate::socket::Config> for Config { |
||||
fn from(config: &crate::socket::Config) -> Config { |
||||
Config { |
||||
write_buffer_size: config.write_buffer_size, |
||||
read_buffer_size: config.read_buffer_size, |
||||
bpf_fd_attempts: config.bpf_fd_attempts, |
||||
read_timeout: config.read_timeout, |
||||
write_timeout: config.write_timeout, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Default for Config { |
||||
fn default() -> Config { |
||||
Config { |
||||
write_buffer_size: 4096, |
||||
read_buffer_size: 4096, |
||||
bpf_fd_attempts: 1000, |
||||
read_timeout: None, |
||||
write_timeout: None, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub fn channel(interface_name: String, config: Config) -> io::Result<crate::socket::Channel> { |
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] |
||||
fn get_fd(_attempts: usize) -> libc::c_int { |
||||
unsafe { |
||||
libc::open( |
||||
CString::new(&b"/dev/bpf"[..]).unwrap().as_ptr(), |
||||
libc::O_RDWR, |
||||
0, |
||||
) |
||||
} |
||||
} |
||||
|
||||
#[cfg(any(target_os = "openbsd", target_os = "macos", target_os = "ios"))] |
||||
fn get_fd(attempts: usize) -> libc::c_int { |
||||
for i in 0..attempts { |
||||
let fd = unsafe { |
||||
let file_name = format!("/dev/bpf{}", i); |
||||
libc::open( |
||||
CString::new(file_name.as_bytes()).unwrap().as_ptr(), |
||||
libc::O_RDWR, |
||||
0, |
||||
) |
||||
}; |
||||
if fd != -1 { |
||||
return fd; |
||||
} |
||||
} |
||||
|
||||
-1 |
||||
} |
||||
|
||||
#[cfg(any(target_os = "freebsd", target_os = "netbsd"))] |
||||
fn set_feedback(fd: libc::c_int) -> io::Result<()> { |
||||
if unsafe { binding::ioctl(fd, binding::BIOCFEEDBACK, &1) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
libc::close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
Ok(()) |
||||
} |
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "openbsd", target_os = "ios"))] |
||||
fn set_feedback(_fd: libc::c_int) -> io::Result<()> { |
||||
Ok(()) |
||||
} |
||||
|
||||
let fd = get_fd(config.bpf_fd_attempts); |
||||
if fd == -1 { |
||||
return Err(io::Error::last_os_error()); |
||||
} |
||||
let mut iface: binding::ifreq = unsafe { mem::zeroed() }; |
||||
for (i, c) in interface_name.bytes().enumerate() { |
||||
iface.ifr_name[i] = c as i8; |
||||
} |
||||
|
||||
let buflen = config.read_buffer_size as libc::c_uint; |
||||
|
||||
if unsafe { binding::ioctl(fd, binding::BIOCSBLEN, &buflen) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
libc::close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
|
||||
if unsafe { binding::ioctl(fd, binding::BIOCSETIF, &iface) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
libc::close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
|
||||
if unsafe { binding::ioctl(fd, binding::BIOCIMMEDIATE, &1) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
libc::close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
|
||||
let mut dlt: libc::c_uint = 0; |
||||
if unsafe { binding::ioctl(fd, binding::BIOCGDLT, &mut dlt) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
libc::close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
|
||||
let mut loopback = false; |
||||
let mut allocated_read_buffer_size = config.read_buffer_size; |
||||
|
||||
if dlt == binding::DLT_NULL { |
||||
loopback = true; |
||||
|
||||
allocated_read_buffer_size += ETHERNET_HEADER_SIZE; |
||||
|
||||
if let Err(e) = set_feedback(fd) { |
||||
return Err(e); |
||||
} |
||||
} else { |
||||
if unsafe { binding::ioctl(fd, binding::BIOCSHDRCMPLT, &1) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
libc::close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
} |
||||
|
||||
if unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) } == -1 { |
||||
let err = io::Error::last_os_error(); |
||||
unsafe { |
||||
close(fd); |
||||
} |
||||
return Err(err); |
||||
} |
||||
|
||||
let fd = Arc::new(FileDesc { fd: fd }); |
||||
let mut sender = Box::new(DataLinkSenderImpl { |
||||
fd: fd.clone(), |
||||
fd_set: unsafe { mem::zeroed() }, |
||||
write_buffer: vec![0; config.write_buffer_size], |
||||
loopback: loopback, |
||||
timeout: config |
||||
.write_timeout |
||||
.map(|to| duration_to_timespec(to)), |
||||
}); |
||||
unsafe { |
||||
libc::FD_ZERO(&mut sender.fd_set as *mut libc::fd_set); |
||||
libc::FD_SET(fd.fd, &mut sender.fd_set as *mut libc::fd_set); |
||||
} |
||||
let mut receiver = Box::new(DataLinkReceiverImpl { |
||||
fd: fd.clone(), |
||||
fd_set: unsafe { mem::zeroed() }, |
||||
read_buffer: vec![0; allocated_read_buffer_size], |
||||
loopback: loopback, |
||||
timeout: config |
||||
.read_timeout |
||||
.map(|to| duration_to_timespec(to)), |
||||
packets: VecDeque::with_capacity(allocated_read_buffer_size / 64), |
||||
}); |
||||
unsafe { |
||||
libc::FD_ZERO(&mut receiver.fd_set as *mut libc::fd_set); |
||||
libc::FD_SET(fd.fd, &mut receiver.fd_set as *mut libc::fd_set); |
||||
} |
||||
Ok(crate::socket::Channel::Ethernet(sender, receiver)) |
||||
} |
||||
|
||||
struct DataLinkSenderImpl { |
||||
fd: Arc<FileDesc>, |
||||
fd_set: libc::fd_set, |
||||
write_buffer: Vec<u8>, |
||||
loopback: bool, |
||||
timeout: Option<libc::timespec>, |
||||
} |
||||
|
||||
impl DataLinkSender for DataLinkSenderImpl { |
||||
#[inline] |
||||
fn build_and_send( |
||||
&mut self, |
||||
num_packets: usize, |
||||
packet_size: usize, |
||||
func: &mut dyn FnMut(&mut [u8]), |
||||
) -> Option<io::Result<()>> { |
||||
let len = num_packets * packet_size; |
||||
if len >= self.write_buffer.len() { |
||||
None |
||||
} else { |
||||
|
||||
let offset = if self.loopback { |
||||
ETHERNET_HEADER_SIZE |
||||
} else { |
||||
0 |
||||
}; |
||||
for chunk in self.write_buffer[..len].chunks_mut(packet_size) { |
||||
func(chunk); |
||||
let ret = unsafe { |
||||
libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set); |
||||
libc::pselect( |
||||
self.fd.fd + 1, |
||||
ptr::null_mut(), |
||||
&mut self.fd_set as *mut libc::fd_set, |
||||
ptr::null_mut(), |
||||
self.timeout |
||||
.as_ref() |
||||
.map(|to| to as *const libc::timespec) |
||||
.unwrap_or(ptr::null()), |
||||
ptr::null(), |
||||
) |
||||
}; |
||||
if ret == -1 { |
||||
return Some(Err(io::Error::last_os_error())); |
||||
} else if ret == 0 { |
||||
return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"))); |
||||
} else { |
||||
match unsafe { |
||||
libc::write( |
||||
self.fd.fd, |
||||
chunk.as_ptr().offset(offset as isize) as *const libc::c_void, |
||||
(chunk.len() - offset) as libc::size_t, |
||||
) |
||||
} { |
||||
len if len == -1 => return Some(Err(io::Error::last_os_error())), |
||||
_ => (), |
||||
} |
||||
} |
||||
} |
||||
Some(Ok(())) |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
fn send_to(&mut self, packet: &[u8], _dst: Option<Interface>) -> Option<io::Result<()>> { |
||||
let offset = if self.loopback { |
||||
ETHERNET_HEADER_SIZE |
||||
} else { |
||||
0 |
||||
}; |
||||
let ret = unsafe { |
||||
libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set); |
||||
libc::pselect( |
||||
self.fd.fd + 1, |
||||
ptr::null_mut(), |
||||
&mut self.fd_set as *mut libc::fd_set, |
||||
ptr::null_mut(), |
||||
self.timeout |
||||
.as_ref() |
||||
.map(|to| to as *const libc::timespec) |
||||
.unwrap_or(ptr::null()), |
||||
ptr::null(), |
||||
) |
||||
}; |
||||
if ret == -1 { |
||||
return Some(Err(io::Error::last_os_error())); |
||||
} else if ret == 0 { |
||||
return Some(Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out"))); |
||||
} else { |
||||
match unsafe { |
||||
libc::write( |
||||
self.fd.fd, |
||||
packet.as_ptr().offset(offset as isize) as *const libc::c_void, |
||||
(packet.len() - offset) as libc::size_t, |
||||
) |
||||
} { |
||||
len if len == -1 => Some(Err(io::Error::last_os_error())), |
||||
_ => Some(Ok(())), |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
struct DataLinkReceiverImpl { |
||||
fd: Arc<FileDesc>, |
||||
fd_set: libc::fd_set, |
||||
read_buffer: Vec<u8>, |
||||
loopback: bool, |
||||
timeout: Option<libc::timespec>, |
||||
packets: VecDeque<(usize, usize)>, |
||||
} |
||||
|
||||
impl DataLinkReceiver for DataLinkReceiverImpl { |
||||
fn next(&mut self) -> io::Result<&[u8]> { |
||||
let (header_size, buffer_offset) = if self.loopback { |
||||
(4, ETHERNET_HEADER_SIZE) |
||||
} else { |
||||
(0, 0) |
||||
}; |
||||
if self.packets.is_empty() { |
||||
let buffer = &mut self.read_buffer[buffer_offset..]; |
||||
let ret = unsafe { |
||||
libc::FD_SET(self.fd.fd, &mut self.fd_set as *mut libc::fd_set); |
||||
libc::pselect( |
||||
self.fd.fd + 1, |
||||
&mut self.fd_set as *mut libc::fd_set, |
||||
ptr::null_mut(), |
||||
ptr::null_mut(), |
||||
self.timeout |
||||
.as_ref() |
||||
.map(|to| to as *const libc::timespec) |
||||
.unwrap_or(ptr::null()), |
||||
ptr::null(), |
||||
) |
||||
}; |
||||
if ret == -1 { |
||||
return Err(io::Error::last_os_error()); |
||||
} else if ret == 0 { |
||||
return Err(io::Error::new(io::ErrorKind::TimedOut, "Timed out")); |
||||
} else { |
||||
let buflen = match unsafe { |
||||
libc::read( |
||||
self.fd.fd, |
||||
buffer.as_ptr() as *mut libc::c_void, |
||||
buffer.len() as libc::size_t, |
||||
) |
||||
} { |
||||
len if len > 0 => len, |
||||
_ => return Err(io::Error::last_os_error()), |
||||
}; |
||||
let mut ptr = buffer.as_mut_ptr(); |
||||
let end = unsafe { buffer.as_ptr().offset(buflen as isize) }; |
||||
while (ptr as *const u8) < end { |
||||
unsafe { |
||||
let packet: *const binding::bpf_hdr = mem::transmute(ptr); |
||||
let start = |
||||
ptr as isize + (*packet).bh_hdrlen as isize - buffer.as_ptr() as isize; |
||||
self.packets.push_back(( |
||||
start as usize + header_size, |
||||
(*packet).bh_caplen as usize - header_size, |
||||
)); |
||||
let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize; |
||||
ptr = ptr.offset(binding::BPF_WORDALIGN(offset)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
let (start, mut len) = self.packets.pop_front().unwrap(); |
||||
len += buffer_offset; |
||||
for i in (&mut self.read_buffer[start..start + buffer_offset]).iter_mut() { |
||||
*i = 0; |
||||
} |
||||
Ok(&self.read_buffer[start..start + len]) |
||||
} |
||||
} |
||||
|
@ -1,107 +0,0 @@ |
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
||||
use crate::gateway::{Gateway}; |
||||
use crate::os; |
||||
|
||||
/// Structure of MAC address
|
||||
#[derive(Clone, Debug)] |
||||
pub struct MacAddr(u8, u8, u8, u8, u8, u8); |
||||
|
||||
impl MacAddr { |
||||
/// Construct a new MacAddr struct from the given octets
|
||||
pub fn new(octets: [u8; 6]) -> MacAddr { |
||||
MacAddr(octets[0], octets[1], octets[2], octets[3], octets[4], octets[5]) |
||||
} |
||||
/// Returns an array of MAC address octets
|
||||
pub fn octets(&self) -> [u8; 6] { |
||||
[self.0,self.1,self.2,self.3,self.4,self.5] |
||||
} |
||||
/// Return a formatted string of MAC address
|
||||
pub fn address(&self) -> String { |
||||
format!("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}", self.0,self.1,self.2,self.3,self.4,self.5) |
||||
} |
||||
pub fn zero() -> MacAddr { |
||||
MacAddr(0,0,0,0,0,0) |
||||
} |
||||
} |
||||
|
||||
impl std::fmt::Display for MacAddr { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
||||
let _ = write!(f,"{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}:{:<02x}",self.0,self.1,self.2,self.3,self.4,self.5); |
||||
Ok(())
|
||||
} |
||||
} |
||||
|
||||
/// Structure of Network Interface information
|
||||
#[derive(Clone, Debug)] |
||||
pub struct Interface { |
||||
pub index: u32, |
||||
pub name: String, |
||||
pub description: Option<String>, |
||||
pub mac_addr: Option<MacAddr>, |
||||
pub ipv4: Vec<Ipv4Addr>, |
||||
pub ipv6: Vec<Ipv6Addr>, |
||||
pub gateway: Option<Gateway>, |
||||
} |
||||
|
||||
/// Get default Network Interface
|
||||
pub fn get_default_interface() -> Result<Interface, String> { |
||||
let local_ip: IpAddr = match os::get_local_ipaddr(){ |
||||
Some(local_ip) => local_ip, |
||||
None => return Err(String::from("Local IP address not found")), |
||||
}; |
||||
let interfaces: Vec<Interface> = os::interfaces(); |
||||
for iface in interfaces { |
||||
match local_ip { |
||||
IpAddr::V4(local_ipv4) => { |
||||
if iface.ipv4.contains(&local_ipv4) { |
||||
return Ok(iface); |
||||
} |
||||
}, |
||||
IpAddr::V6(local_ipv6) => { |
||||
if iface.ipv6.contains(&local_ipv6) { |
||||
return Ok(iface); |
||||
} |
||||
}, |
||||
} |
||||
} |
||||
Err(String::from("Default Interface not found")) |
||||
} |
||||
|
||||
/// Get default Network Interface index
|
||||
pub fn get_default_interface_index() -> Option<u32> { |
||||
os::default_interface_index() |
||||
} |
||||
|
||||
/// Get default Network Interface name
|
||||
pub fn get_default_interface_name() -> Option<String> { |
||||
os::default_interface_name() |
||||
} |
||||
|
||||
/// Get a list of available Network Interfaces
|
||||
pub fn get_interfaces() -> Vec<Interface> { |
||||
os::interfaces() |
||||
} |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
#[test] |
||||
fn test_interfaces() { |
||||
let interfaces = get_interfaces(); |
||||
for interface in interfaces { |
||||
println!("{:#?}", interface); |
||||
} |
||||
} |
||||
#[test] |
||||
fn test_default_interface() { |
||||
println!("{:#?}", get_default_interface()); |
||||
} |
||||
#[test] |
||||
fn test_default_interface_index() { |
||||
println!("{:?}", get_default_interface_index()); |
||||
} |
||||
#[test] |
||||
fn test_default_interface_name() { |
||||
println!("{:?}", get_default_interface_name()); |
||||
} |
||||
} |
@ -0,0 +1,77 @@ |
||||
pub mod packet; |
||||
|
||||
#[cfg(not(target_os="windows"))] |
||||
mod unix; |
||||
#[cfg(not(target_os="windows"))] |
||||
pub use self::unix::*; |
||||
|
||||
#[cfg(test)] |
||||
mod tests { |
||||
use super::*; |
||||
use std::net::UdpSocket; |
||||
#[test] |
||||
fn test_packet_capture() { |
||||
let interface_name: String = String::from("en0"); |
||||
let config = Config { |
||||
write_buffer_size: 4096, |
||||
read_buffer_size: 4096, |
||||
read_timeout: None, |
||||
write_timeout: None, |
||||
channel_type: ChannelType::Layer2, |
||||
bpf_fd_attempts: 1000, |
||||
linux_fanout: None, |
||||
promiscuous: false, |
||||
}; |
||||
let (mut _tx, mut rx) = match channel(interface_name, config) { |
||||
Ok(Channel::Ethernet(tx, rx)) => (tx, rx), |
||||
//Ok(_) => panic!("Unknown channel type"),
|
||||
Err(e) => panic!("Error happened {}", e), |
||||
}; |
||||
|
||||
let buf = [0u8; 0]; |
||||
let socket = match UdpSocket::bind("0.0.0.0:0") { |
||||
Ok(s) => s, |
||||
Err(e) => { |
||||
println!("Failed to create UDP socket {}", e); |
||||
return; |
||||
}, |
||||
}; |
||||
let dst: &str = "1.1.1.1:80"; |
||||
match socket.set_ttl(1) { |
||||
Ok(_) => (), |
||||
Err(e) => { |
||||
println!("Failed to set TTL {}", e); |
||||
return; |
||||
}, |
||||
} |
||||
match socket.send_to(&buf, dst) { |
||||
Ok(_) => (), |
||||
Err(e) => { |
||||
println!("Failed to send data {}", e); |
||||
return; |
||||
}, |
||||
} |
||||
|
||||
loop { |
||||
match rx.next() { |
||||
Ok(frame) => { |
||||
match packet::parse_frame(frame){ |
||||
Ok(gateway) => { |
||||
println!("Default Gateway:"); |
||||
println!("{}", gateway.mac_addr); |
||||
println!("{}", gateway.ip_addr); |
||||
return; |
||||
}, |
||||
Err(_) => { |
||||
println!("Parse Error"); |
||||
}, |
||||
} |
||||
}, |
||||
Err(e) => { |
||||
println!("{}", e); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,115 @@ |
||||
use std::convert::TryInto; |
||||
use crate::gateway::Gateway; |
||||
use crate::interface::MacAddr; |
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; |
||||
use std::u16; |
||||
|
||||
pub const ETHER_TYPE_IPV4: [u8; 2] = [8, 0]; |
||||
pub const ETHER_TYPE_IPV6: [u8; 2] = [134, 221]; |
||||
pub const NEXT_HEADER_ICMP: u8 = 1; |
||||
pub const NEXT_HEADER_ICMPV6: u8 = 58; |
||||
pub const ICMP_TYPE_TIME_EXCEEDED: u8 = 11; |
||||
pub const ICMPV6_TYPE_TIME_EXCEEDED: u8 = 3; |
||||
|
||||
pub enum Frame { |
||||
SrcMacAddr, |
||||
DstMacAddr, |
||||
EtherType, |
||||
SrcIpv4Addr, |
||||
DstIpv4Addr, |
||||
SrcIpv6Addr, |
||||
DstIpv6Addr, |
||||
NextHeaderProtocolIpv4, |
||||
NextHeaderProtocolIpv6, |
||||
IcmpType, |
||||
Icmpv6Type, |
||||
} |
||||
|
||||
impl Frame { |
||||
fn start_index(&self) -> usize { |
||||
match *self { |
||||
Frame::SrcMacAddr => 6, |
||||
Frame::DstMacAddr => 0, |
||||
Frame::EtherType => 12, |
||||
Frame::SrcIpv4Addr => 26, |
||||
Frame::DstIpv4Addr => 30, |
||||
Frame::SrcIpv6Addr => 22, |
||||
Frame::DstIpv6Addr => 38, |
||||
Frame::NextHeaderProtocolIpv4 => 23, |
||||
Frame::NextHeaderProtocolIpv6 => 20, |
||||
Frame::IcmpType => 34, |
||||
Frame::Icmpv6Type => 54, |
||||
} |
||||
} |
||||
fn end_index(&self) -> usize { |
||||
match *self { |
||||
Frame::SrcMacAddr => 12, |
||||
Frame::DstMacAddr => 6, |
||||
Frame::EtherType => 14, |
||||
Frame::SrcIpv4Addr => 30, |
||||
Frame::DstIpv4Addr => 34, |
||||
Frame::SrcIpv6Addr => 38, |
||||
Frame::DstIpv6Addr => 54, |
||||
Frame::NextHeaderProtocolIpv4 => 24, |
||||
Frame::NextHeaderProtocolIpv6 => 21, |
||||
Frame::IcmpType => 35, |
||||
Frame::Icmpv6Type => 55, |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn convert_ipv4_bytes(bytes: [u8; 4]) -> Ipv4Addr { |
||||
Ipv4Addr::new(bytes[0], bytes[1], bytes[2], bytes[3]) |
||||
} |
||||
|
||||
fn convert_ipv6_bytes(bytes: [u8; 16]) -> Ipv6Addr { |
||||
let h1: u16 = ((bytes[0] as u16) << 8) | bytes[1] as u16; |
||||
let h2: u16 = ((bytes[2] as u16) << 8) | bytes[3] as u16; |
||||
let h3: u16 = ((bytes[4] as u16) << 8) | bytes[5] as u16; |
||||
let h4: u16 = ((bytes[6] as u16) << 8) | bytes[7] as u16; |
||||
let h5: u16 = ((bytes[8] as u16) << 8) | bytes[9] as u16; |
||||
let h6: u16 = ((bytes[10] as u16) << 8) | bytes[11] as u16; |
||||
let h7: u16 = ((bytes[12] as u16) << 8) | bytes[13] as u16; |
||||
let h8: u16 = ((bytes[14] as u16) << 8) | bytes[15] as u16; |
||||
Ipv6Addr::new(h1, h2, h3, h4, h5, h6, h7, h8) |
||||
} |
||||
|
||||
pub fn parse_frame(frame: &[u8]) -> Result<Gateway, ()> { |
||||
let src_mac: [u8; 6] = frame[Frame::SrcMacAddr.start_index()..Frame::SrcMacAddr.end_index()].try_into().unwrap(); |
||||
let ether_type: [u8; 2] = frame[Frame::EtherType.start_index()..Frame::EtherType.end_index()].try_into().unwrap(); |
||||
match ether_type { |
||||
ETHER_TYPE_IPV4 => { |
||||
let src_ip: [u8; 4] = frame[Frame::SrcIpv4Addr.start_index()..Frame::SrcIpv4Addr.end_index()].try_into().unwrap(); |
||||
let next_header_protocol: u8 = frame[Frame::NextHeaderProtocolIpv4.start_index()]; |
||||
if next_header_protocol == NEXT_HEADER_ICMP { |
||||
let icmp_type: u8 = frame[Frame::IcmpType.start_index()]; |
||||
if icmp_type == ICMP_TYPE_TIME_EXCEEDED { |
||||
let gateway = Gateway { |
||||
mac_addr: MacAddr::new(src_mac), |
||||
ip_addr: IpAddr::V4(convert_ipv4_bytes(src_ip)), |
||||
}; |
||||
return Ok(gateway); |
||||
} |
||||
} |
||||
}, |
||||
ETHER_TYPE_IPV6 => { |
||||
let src_ip: [u8; 16] = frame[Frame::SrcIpv6Addr.start_index()..Frame::SrcIpv6Addr.end_index()].try_into().unwrap(); |
||||
let next_header_protocol: u8 = frame[Frame::NextHeaderProtocolIpv6.start_index()]; |
||||
if next_header_protocol == NEXT_HEADER_ICMPV6 { |
||||
let icmp_type: u8 = frame[Frame::Icmpv6Type.start_index()]; |
||||
if icmp_type == ICMPV6_TYPE_TIME_EXCEEDED { |
||||
let icmp_type: u8 = frame[Frame::Icmpv6Type.start_index()]; |
||||
if icmp_type == ICMPV6_TYPE_TIME_EXCEEDED { |
||||
let gateway = Gateway { |
||||
mac_addr: MacAddr::new(src_mac), |
||||
ip_addr: IpAddr::V6(convert_ipv6_bytes(src_ip)), |
||||
}; |
||||
return Ok(gateway); |
||||
} |
||||
} |
||||
} |
||||
}, |
||||
_ => {}, |
||||
} |
||||
Err(()) |
||||
} |
@ -0,0 +1,85 @@ |
||||
use std::io; |
||||
use std::time::Duration; |
||||
|
||||
use crate::bpf; |
||||
use crate::interface::Interface; |
||||
|
||||
pub type EtherType = u16; |
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
||||
pub enum ChannelType { |
||||
Layer2, |
||||
Layer3(EtherType), |
||||
} |
||||
|
||||
#[non_exhaustive] |
||||
pub enum Channel { |
||||
Ethernet(Box<dyn DataLinkSender>, Box<dyn DataLinkReceiver>), |
||||
} |
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
||||
pub enum FanoutType { |
||||
HASH, |
||||
LB, |
||||
CPU, |
||||
ROLLOVER, |
||||
RND, |
||||
QM, |
||||
CBPF, |
||||
EBPF, |
||||
} |
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
||||
pub struct FanoutOption { |
||||
pub group_id: u16, |
||||
pub fanout_type: FanoutType, |
||||
pub defrag: bool, |
||||
pub rollover: bool, |
||||
} |
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
||||
pub struct Config { |
||||
pub write_buffer_size: usize, |
||||
pub read_buffer_size: usize, |
||||
pub read_timeout: Option<Duration>, |
||||
pub write_timeout: Option<Duration>, |
||||
pub channel_type: ChannelType, |
||||
pub bpf_fd_attempts: usize, |
||||
pub linux_fanout: Option<FanoutOption>, |
||||
pub promiscuous: bool, |
||||
} |
||||
|
||||
impl Default for Config { |
||||
fn default() -> Config { |
||||
Config { |
||||
write_buffer_size: 4096, |
||||
read_buffer_size: 4096, |
||||
read_timeout: None, |
||||
write_timeout: None, |
||||
channel_type: ChannelType::Layer2, |
||||
bpf_fd_attempts: 1000, |
||||
linux_fanout: None, |
||||
promiscuous: true, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[inline] |
||||
pub fn channel(interface_name: String, configuration: Config) -> io::Result<Channel> { |
||||
bpf::channel(interface_name, (&configuration).into()) |
||||
} |
||||
|
||||
pub trait DataLinkSender: Send { |
||||
fn build_and_send( |
||||
&mut self, |
||||
num_packets: usize, |
||||
packet_size: usize, |
||||
func: &mut dyn FnMut(&mut [u8]), |
||||
) -> Option<io::Result<()>>; |
||||
|
||||
fn send_to(&mut self, packet: &[u8], dst: Option<Interface>) -> Option<io::Result<()>>; |
||||
} |
||||
|
||||
pub trait DataLinkReceiver: Send { |
||||
fn next(&mut self) -> io::Result<&[u8]>; |
||||
} |
Loading…
Reference in new issue