Impl receiver

main
shellrow 3 years ago
parent 3afc519414
commit b3172f19e1
  1. 111
      src/bpf/binding.rs
  2. 6
      src/bpf/mod.rs
  3. 416
      src/bpf/unix.rs
  4. 11
      src/gateway/mod.rs
  5. 0
      src/gateway/unix.rs
  6. 107
      src/interface1.rs
  7. 2
      src/lib.rs
  8. 77
      src/socket/mod.rs
  9. 115
      src/socket/packet.rs
  10. 85
      src/socket/unix.rs
  11. 2
      src/sys/unix.rs

@ -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"))] #[cfg(not(target_os="windows"))]
mod unix; mod unix;
#[cfg(not(target_os="windows"))] #[cfg(not(target_os="windows"))]
pub use self::unix::*; pub use self::unix::*;

@ -1,17 +1,401 @@
#![allow(non_camel_case_types)] use super::binding;
#![allow(non_snake_case)] use crate::socket::{DataLinkReceiver, DataLinkSender};
#![allow(dead_code)] use crate::interface::Interface;
pub const AF_LINK: libc::c_int = 18; use std::collections::VecDeque;
use std::ffi::CString;
#[cfg(any(target_os = "openbsd", target_os = "freebsd", target_os = "netbsd", target_os = "macos", target_os = "ios"))] use std::io;
pub struct sockaddr_dl { use std::mem;
pub sdl_len: libc::c_uchar, use std::ptr;
pub sdl_family: libc::c_uchar, use std::sync::Arc;
pub sdl_index: libc::c_ushort, use std::time::Duration;
pub sdl_type: libc::c_uchar,
pub sdl_nlen: libc::c_uchar, static ETHERNET_HEADER_SIZE: usize = 14;
pub sdl_alen: libc::c_uchar,
pub sdl_slen: libc::c_uchar, pub type CSocket = libc::c_int;
pub sdl_data: [libc::c_char; 46], 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,4 +1,4 @@
use std::net::IpAddr; use std::net::{IpAddr, Ipv4Addr};
use crate::interface::{MacAddr, Interface}; use crate::interface::{MacAddr, Interface};
use crate::os; use crate::os;
@ -9,6 +9,15 @@ pub struct Gateway {
pub ip_addr: IpAddr, pub ip_addr: IpAddr,
} }
impl Gateway {
pub fn new() -> Gateway {
Gateway {
mac_addr: MacAddr::zero(),
ip_addr: IpAddr::V4(Ipv4Addr::LOCALHOST),
}
}
}
/// Get default Gateway /// Get default Gateway
pub fn get_default_gateway() -> Result<Gateway, String> { pub fn get_default_gateway() -> Result<Gateway, String> {
let local_ip: IpAddr = match os::get_local_ipaddr(){ let local_ip: IpAddr = match os::get_local_ipaddr(){

@ -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());
}
}

@ -1,6 +1,8 @@
mod os; mod os;
mod sys; mod sys;
mod bpf; mod bpf;
mod socket;
pub mod interface; pub mod interface;
pub mod gateway; pub mod gateway;

@ -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]>;
}

@ -51,7 +51,7 @@ pub fn sockaddr_to_addr(storage: &SockAddrStorage, len: usize) -> io::Result<Soc
storage.sin6_scope_id, storage.sin6_scope_id,
))) )))
} }
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "expected IPv4 or IPv6 socket",)), _ => Err(io::Error::new(io::ErrorKind::InvalidData, "Not supported",)),
} }
} }

Loading…
Cancel
Save