diff --git a/examples/async_read.rs b/examples/async_read.rs index 4e5b79a..4a930dc 100644 --- a/examples/async_read.rs +++ b/examples/async_read.rs @@ -6,9 +6,18 @@ fn main() { future::block_on(async { let mut ps = RawPacketStream::new().unwrap(); loop { + #[cfg(target_os = "linux")] let mut buf = [0u8; 1500]; - ps.read(&mut buf).await.unwrap(); - println!("{}", buf.to_hex(24)); + #[cfg(target_os = "macos")] + let mut buf = [0u8; 4096]; + + #[cfg(target_os = "linux")] + ps.bind("lo"); + #[cfg(target_os = "macos")] + ps.bind("lo0"); + + let read = ps.read(&mut buf).await.unwrap(); + println!("{}", (&buf[..read]).to_hex(24)); } }) } diff --git a/examples/sync_read.rs b/examples/sync_read.rs index fa4325d..9ac7992 100644 --- a/examples/sync_read.rs +++ b/examples/sync_read.rs @@ -1,11 +1,22 @@ +use std::io::Read; + use afpacket::sync::RawPacketStream; use nom::HexDisplay; fn main() { let mut ps = RawPacketStream::new().unwrap(); loop { + #[cfg(target_os = "linux")] let mut buf = [0u8; 1500]; - ps.read(&mut buf).unwrap(); - println!("{}", buf.to_hex(24)); + #[cfg(target_os = "macos")] + let mut buf = [0u8; 4096]; + + #[cfg(target_os = "linux")] + ps.bind("lo"); + #[cfg(target_os = "macos")] + ps.bind("lo0"); + + let read = ps.read(&mut buf).unwrap(); + println!("{}", (&buf[..read]).to_hex(24)); } } diff --git a/src/async.rs b/src/async.rs index aefd327..b34eda5 100644 --- a/src/async.rs +++ b/src/async.rs @@ -1,16 +1,17 @@ -use std::task::{Context, Poll}; -use std::io::Result; -use std::pin::Pin; -use std::sync::Arc; -use std::os::unix::prelude::{AsRawFd, FromRawFd, RawFd}; use super::sync::RawPacketStream as SyncRawPacketStream; pub use super::sync::{Filter, FilterProgram}; -use futures_lite::io::{AsyncRead, AsyncWrite}; use async_io::Async; +use futures_lite::io::{AsyncRead, AsyncWrite}; +use std::io::Result; +use std::os::unix::prelude::{AsRawFd, FromRawFd, RawFd}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; #[derive(Debug, Clone)] pub struct RawPacketStream(Arc>); +#[cfg(any(target_family = "unix", doc))] impl RawPacketStream { pub fn new() -> Result { Ok(SyncRawPacketStream::new()?.into()) @@ -20,21 +21,27 @@ impl RawPacketStream { (&*self.0).as_ref().bind_internal(name) } + // TODO: add darwin + #[cfg(any(target_os = "linux", doc))] pub fn set_promisc(&mut self, name: &str, state: bool) -> Result<()> { (&*self.0).as_ref().set_promisc_internal(name, state) } + // TODO: add darwin + #[cfg(any(target_os = "linux", doc))] pub fn set_bpf_filter(&mut self, filter: FilterProgram) -> Result<()> { (&*self.0).as_ref().set_bpf_filter_internal(filter) } } +#[cfg(any(target_family = "unix", doc))] impl AsyncRead for RawPacketStream { fn poll_read(self: Pin<&mut Self>, ctx: &mut Context, buf: &mut [u8]) -> Poll> { Pin::new(&mut &*self.0).poll_read(ctx, buf) } } +#[cfg(any(target_family = "unix", doc))] impl AsyncWrite for RawPacketStream { fn poll_write(self: Pin<&mut Self>, ctx: &mut Context, buf: &[u8]) -> Poll> { Pin::new(&mut &*self.0).poll_write(ctx, buf) @@ -47,18 +54,21 @@ impl AsyncWrite for RawPacketStream { } } +#[cfg(any(target_family = "unix", doc))] impl From for RawPacketStream { fn from(sync: SyncRawPacketStream) -> RawPacketStream { RawPacketStream(Arc::new(Async::new(sync).expect("oopsie whoopsie"))) } } +#[cfg(any(target_family = "unix", doc))] impl AsRawFd for RawPacketStream { fn as_raw_fd(&self) -> RawFd { (&*self.0).get_ref().as_raw_fd() } } +#[cfg(any(target_family = "unix", doc))] impl FromRawFd for RawPacketStream { unsafe fn from_raw_fd(fd: RawFd) -> RawPacketStream { SyncRawPacketStream::from_raw_fd(fd).into() diff --git a/src/sync/darwin.rs b/src/sync/darwin.rs new file mode 100644 index 0000000..7babf67 --- /dev/null +++ b/src/sync/darwin.rs @@ -0,0 +1,160 @@ +use std::io::{Error, ErrorKind, Read, Result, Write}; +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + +use libc::*; + +use super::*; +use std::fmt::Arguments; + +impl RawPacketStream { + pub fn new() -> Result { + let mut i = 0; + let fd = loop { + let path = format!("/dev/bpf{}\0", i); + let fd = unsafe { open(path.as_ptr() as *const _, O_RDWR) }; + if fd != -1 { + break fd; + } + i += 1; + if i >= 100 { + return Err(Error::last_os_error()); + } + }; + + // buffer length has to be retrieved to make the socket active + let mut length = 0u32; + if unsafe { ioctl(fd, BIOCGBLEN as _, &mut length as *mut _) } == -1 { + return Err(Error::last_os_error()); + } + + Ok(RawPacketStream(fd as RawFd)) + } + + pub(crate) fn bind_internal(&self, if_name: &str) -> Result<()> { + let mut ifr = ifreq { + ifr_name: [0; IF_NAMESIZE], + ifr_ifru: IfrIfru { ifru_mtu: 0 }, + }; + + ifr.ifr_name[..if_name.len()].copy_from_slice(if_name.as_ref()); + + if unsafe { ioctl(self.0, BIOCSETIF as _, &ifr) } == -1 { + return Err(Error::last_os_error()); + } + + // set imidiat mode + let value = 1; + if unsafe { ioctl(self.0, BIOCIMMEDIATE as _, &value) } == -1 { + return Err(Error::last_os_error()); + } + + Ok(()) + } + + pub(crate) fn set_promisc_internal(&self, state: bool) -> Result<()> { + let value = if state { 1 } else { 0 }; + if unsafe { ioctl(self.0, BIOCPROMISC as _, &value) } == -1 { + return Err(Error::last_os_error()); + } + Ok(()) + } + + pub fn get_buffer_size(&self) -> Result { + let mut length = 0u32; + if unsafe { ioctl(self.0, BIOCGBLEN as _, &mut length as *mut _) } == -1 { + return Err(Error::last_os_error()); + } + Ok(length) + } +} + +fn read_fd(fd: RawFd, buf: &mut [u8]) -> Result { + let rv = unsafe { read(fd, buf.as_mut_ptr() as *mut _, buf.len()) }; + if rv < 0 { + return Err(Error::last_os_error()); + } + + Ok(rv as usize) +} + +impl Read for RawPacketStream { + fn read(&mut self, buf: &mut [u8]) -> Result { + read_fd(self.0, buf) + } +} + +impl<'a> Read for &'a RawPacketStream { + fn read(&mut self, buf: &mut [u8]) -> Result { + read_fd(self.0, buf) + } +} + +fn write_fd(fd: RawFd, buf: &[u8]) -> Result { + let rv = unsafe { libc::write(fd, buf.as_ptr() as *const _, buf.len()) }; + if rv < 0 { + return Err(Error::last_os_error()); + } + + Ok(rv as usize) +} + +fn flush_fd(fd: RawFd) -> Result<()> { + if unsafe { ioctl(fd, BIOCFLUSH as _) } == -1 { + return Err(Error::last_os_error()); + } + Ok(()) +} + +impl Write for RawPacketStream { + fn write(&mut self, buf: &[u8]) -> Result { + write_fd(self.0, buf) + } + + fn flush(&mut self) -> Result<()> { + flush_fd(self.0) + } +} + +impl<'a> Write for &'a RawPacketStream { + fn write(&mut self, buf: &[u8]) -> Result { + write_fd(self.0, buf) + } + + fn flush(&mut self) -> Result<()> { + flush_fd(self.0) + } +} + +impl Drop for RawPacketStream { + fn drop(&mut self) { + unsafe { close(self.0) }; + } +} + +#[repr(C)] +union IfrIfru { + ifru_addr: sockaddr, + ifru_addr_v4: sockaddr_in, + ifru_addr_v6: sockaddr_in, + ifru_dstaddr: sockaddr, + ifru_broadaddr: sockaddr, + ifru_flags: c_short, + ifru_metric: c_int, + ifru_mtu: c_int, + ifru_phys: c_int, + ifru_media: c_int, + ifru_intval: c_int, + //ifru_data: caddr_t, + //ifru_devmtu: ifdevmtu, + //ifru_kpi: ifkpi, + ifru_wake_flags: u32, + ifru_route_refcnt: u32, + ifru_cap: [c_int; 2], + ifru_functional_type: u32, +} + +#[repr(C)] +pub struct ifreq { + ifr_name: [c_uchar; IF_NAMESIZE], + ifr_ifru: IfrIfru, +} diff --git a/src/sync.rs b/src/sync/linux.rs similarity index 63% rename from src/sync.rs rename to src/sync/linux.rs index d40abbc..1955f38 100644 --- a/src/sync.rs +++ b/src/sync/linux.rs @@ -1,46 +1,13 @@ -// Derived from the mio-afpacket crate by Alexander Polakov , -// licensed under the MIT license. https://github.com/polachok/mio-afpacket - use std::io::{Error, ErrorKind, Read, Result, Write}; use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use libc::{sockaddr_ll, sockaddr_storage, socket, packet_mreq, setsockopt}; -use libc::{AF_PACKET, ETH_P_ALL, SOCK_RAW, SOL_PACKET, SOL_SOCKET, PACKET_MR_PROMISC, - SO_ATTACH_FILTER, PACKET_ADD_MEMBERSHIP, PACKET_DROP_MEMBERSHIP, MSG_DONTWAIT}; - -/// Packet sockets are used to receive or send raw packets at OSI 2 level. -#[derive(Debug, Clone)] -pub struct RawPacketStream(RawFd); +use libc::{packet_mreq, setsockopt, sockaddr_ll, sockaddr_storage, socket}; +use libc::{ + AF_PACKET, ETH_P_ALL, MSG_DONTWAIT, PACKET_ADD_MEMBERSHIP, PACKET_DROP_MEMBERSHIP, + PACKET_MR_PROMISC, SOCK_RAW, SOL_PACKET, SOL_SOCKET, SO_ATTACH_FILTER, +}; -pub type Filter = (u16, u8, u8, u32); -pub type FilterProgram = Vec; - -#[derive(Debug, Clone)] -#[repr(C)] -struct sock_filter { - code: u16, - jt: u8, - jf: u8, - k: u32, -} - -#[derive(Debug, Clone)] -#[repr(C)] -struct sock_fprog { - len: u16, - filter: *const sock_filter, -} - -impl From for sock_filter { - fn from(f: Filter) -> sock_filter { - sock_filter { - code: f.0, - jt: f.1, - jf: f.2, - k: f.3, - } - } -} +use super::*; impl RawPacketStream { /// Create new raw packet stream binding to all interfaces @@ -52,11 +19,6 @@ impl RawPacketStream { Ok(RawPacketStream(fd as RawFd)) } - /// Bind socket to an interface (by name). - pub fn bind(&mut self, name: &str) -> Result<()> { - self.bind_internal(name) - } - // should take an &mut to unsure not just anyone can call it, // but async wrapper needs this variant pub(crate) fn bind_internal(&self, name: &str) -> Result<()> { @@ -64,7 +26,7 @@ impl RawPacketStream { self.bind_by_index(idx) } - fn bind_by_index(&self, ifindex: i32) -> Result<()> { + pub(crate) fn bind_by_index(&self, ifindex: i32) -> Result<()> { unsafe { let mut ss: sockaddr_storage = std::mem::zeroed(); let sll: *mut sockaddr_ll = &mut ss as *mut sockaddr_storage as *mut sockaddr_ll; @@ -81,10 +43,6 @@ impl RawPacketStream { Ok(()) } - pub fn set_promisc(&mut self, name: &str, state: bool) -> Result<()> { - self.set_promisc_internal(name, state) - } - // should take an &mut to unsure not just anyone can call it, // but async wrapper needs this variant pub(crate) fn set_promisc_internal(&self, name: &str, state: bool) -> Result<()> { @@ -102,7 +60,13 @@ impl RawPacketStream { mreq.mr_ifindex = idx; mreq.mr_type = PACKET_MR_PROMISC as u16; - let res = setsockopt(self.0, SOL_PACKET, packet_membership, (&mreq as *const packet_mreq) as *const libc::c_void, std::mem::size_of::() as u32); + let res = setsockopt( + self.0, + SOL_PACKET, + packet_membership, + (&mreq as *const packet_mreq) as *const libc::c_void, + std::mem::size_of::() as u32, + ); if res == -1 { return Err(Error::last_os_error()); } @@ -111,10 +75,6 @@ impl RawPacketStream { Ok(()) } - pub fn set_bpf_filter(&mut self, filter: FilterProgram) -> Result<()> { - self.set_bpf_filter_internal(filter) - } - pub(crate) fn set_bpf_filter_internal(&self, filter: FilterProgram) -> Result<()> { let filters: Vec = filter.into_iter().map(|x| x.into()).collect(); let program = sock_fprog { @@ -123,7 +83,13 @@ impl RawPacketStream { }; unsafe { - let res = setsockopt(self.0, SOL_SOCKET, SO_ATTACH_FILTER, &program as *const _ as *const libc::c_void, std::mem::size_of::() as u32); + let res = setsockopt( + self.0, + SOL_SOCKET, + SO_ATTACH_FILTER, + &program as *const _ as *const libc::c_void, + std::mem::size_of::() as u32, + ); if res == -1 { return Err(Error::last_os_error()); } @@ -135,8 +101,17 @@ impl RawPacketStream { pub fn drain(&mut self) { let mut buf = [0u8; 1]; loop { - let rv = unsafe { libc::recv(self.0, buf.as_mut_ptr() as *mut libc::c_void, buf.len(), MSG_DONTWAIT) }; - if rv == -1 { break; } + let rv = unsafe { + libc::recv( + self.0, + buf.as_mut_ptr() as *mut libc::c_void, + buf.len(), + MSG_DONTWAIT, + ) + }; + if rv == -1 { + break; + } } } } @@ -146,7 +121,7 @@ fn index_by_name(name: &str) -> Result { return Err(ErrorKind::InvalidInput.into()); } let mut buf = [0u8; libc::IFNAMSIZ]; - buf[..name.len()].copy_from_slice(name.as_bytes()); + buf[..name.len()].copy_from_slice(name.as_bytes()); let idx = unsafe { libc::if_nametoindex(buf.as_ptr() as *const libc::c_char) }; if idx == 0 { return Err(Error::last_os_error()); @@ -205,24 +180,6 @@ impl<'a> Write for &'a RawPacketStream { } } -impl IntoRawFd for RawPacketStream { - fn into_raw_fd(self) -> RawFd { - self.0 - } -} - -impl AsRawFd for RawPacketStream { - fn as_raw_fd(&self) -> RawFd { - self.0 - } -} - -impl FromRawFd for RawPacketStream { - unsafe fn from_raw_fd(fd: RawFd) -> RawPacketStream { - RawPacketStream(fd) - } -} - impl Drop for RawPacketStream { fn drop(&mut self) { unsafe { diff --git a/src/sync/mod.rs b/src/sync/mod.rs new file mode 100644 index 0000000..3a7fbb7 --- /dev/null +++ b/src/sync/mod.rs @@ -0,0 +1,91 @@ +// Derived from the mio-afpacket crate by Alexander Polakov , +// licensed under the MIT license. https://github.com/polachok/mio-afpacket + +#[cfg(target_os = "macos")] +mod darwin; +#[cfg(target_os = "linux")] +mod linux; + +use std::io::{Error, ErrorKind, Read, Result, Write}; +#[cfg(any(target_family = "unix", doc))] +use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; + +/// Packet sockets are used to receive or send raw packets at OSI 2 level. +#[cfg(any(target_family = "unix", doc))] +#[derive(Debug, Clone)] +pub struct RawPacketStream(RawFd); + +pub type Filter = (u16, u8, u8, u32); +pub type FilterProgram = Vec; + +#[derive(Debug, Clone)] +#[repr(C)] +struct sock_filter { + code: u16, + jt: u8, + jf: u8, + k: u32, +} + +#[derive(Debug, Clone)] +#[repr(C)] +struct sock_fprog { + len: u16, + filter: *const sock_filter, +} + +impl From for sock_filter { + fn from(f: Filter) -> sock_filter { + sock_filter { + code: f.0, + jt: f.1, + jf: f.2, + k: f.3, + } + } +} + +impl RawPacketStream { + /// Bind socket to an interface (by name). + pub fn bind(&mut self, name: &str) -> Result<()> { + self.bind_internal(name) + } + + // TODO: more oses + #[cfg(any(target_os = "linux", doc))] + pub fn set_promisc(&mut self, name: &str, state: bool) -> Result<()> { + self.set_promisc_internal(name, state) + } + + #[cfg(any(target_os = "macos", doc))] + pub fn set_promisc(&mut self, state: bool) -> Result<()> { + self.set_promisc_internal(state) + } + + // TODO: more oses + #[cfg(any(target_os = "linux", doc))] + pub fn set_bpf_filter(&mut self, filter: FilterProgram) -> Result<()> { + self.set_bpf_filter_internal(filter) + } +} + +#[cfg(any(target_family = "unix", doc))] +impl IntoRawFd for RawPacketStream { + fn into_raw_fd(self) -> RawFd { + self.0 + } +} + +#[cfg(any(target_family = "unix", doc))] +impl AsRawFd for RawPacketStream { + fn as_raw_fd(&self) -> RawFd { + self.0 + } +} + +#[cfg(any(target_family = "unix", doc))] +impl FromRawFd for RawPacketStream { + unsafe fn from_raw_fd(fd: RawFd) -> RawPacketStream { + RawPacketStream(fd) + } +}