123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- use std::io;
- use std::os::unix::io::{AsRawFd, RawFd};
- use libc;
- use super::{ifreq, ifreq_for};
- /// set interface
- #[cfg(target_os = "macos")]
- const BIOCSETIF: libc::c_ulong = 0x8020426c;
- /// get buffer length
- #[cfg(target_os = "macos")]
- const BIOCGBLEN: libc::c_ulong = 0x40044266;
- /// set immediate/nonblocking read
- #[cfg(target_os = "macos")]
- const BIOCIMMEDIATE: libc::c_ulong = 0x80044270;
- // TODO: check if this is same for OSes other than macos
- #[cfg(target_os = "macos")]
- const BPF_HDRLEN: usize = 18;
- macro_rules! try_ioctl {
- ($fd:expr,$cmd:expr,$req:expr) => {
- unsafe {
- if libc::ioctl($fd, $cmd, $req) == -1 {
- return Err(io::Error::last_os_error());
- }
- }
- };
- }
- #[derive(Debug)]
- pub struct BpfDevice {
- fd: libc::c_int,
- ifreq: ifreq,
- }
- impl AsRawFd for BpfDevice {
- fn as_raw_fd(&self) -> RawFd {
- self.fd
- }
- }
- fn open_device() -> io::Result<libc::c_int> {
- unsafe {
- for i in 0..256 {
- let dev = format!("/dev/bpf{}", i).as_ptr() as *const libc::c_char;
- match libc::open(dev, libc::O_RDWR) {
- -1 => continue,
- fd => return Ok(fd),
- };
- }
- }
- // at this point, all 256 BPF devices were busy and we weren't able to open any
- Err(io::Error::last_os_error())
- }
- impl BpfDevice {
- pub fn new(name: &str) -> io::Result<BpfDevice> {
- Ok(BpfDevice {
- fd: open_device()?,
- ifreq: ifreq_for(name),
- })
- }
- pub fn bind_interface(&mut self) -> io::Result<()> {
- try_ioctl!(self.fd, BIOCSETIF, &mut self.ifreq);
- Ok(())
- }
- /// This in fact does not return the interface's mtu,
- /// but it returns the size of the buffer that the app needs to allocate
- /// for the BPF device
- ///
- /// The `SIOGIFMTU` cant be called on a BPF descriptor. There is a workaround
- /// to get the actual interface mtu, but this should work better
- ///
- /// To get the interface MTU, you would need to create a raw socket first,
- /// and then call `SIOGIFMTU` for the same interface your BPF device is "bound" to.
- /// This MTU that you would get would not include the length of `struct bpf_hdr`
- /// which gets prepended to every packet by BPF,
- /// and your packet will be truncated if it has the length of the MTU.
- ///
- /// The buffer size for BPF is usually 4096 bytes, MTU is typically 1500 bytes.
- /// You could do something like `mtu += BPF_HDRLEN`,
- /// but you must change the buffer size the BPF device expects using `BIOCSBLEN` accordingly,
- /// and you must set it before setting the interface with the `BIOCSETIF` ioctl.
- ///
- /// The reason I said this should work better is because you might see some unexpected behavior,
- /// truncated/unaligned packets, I/O errors on read()
- /// if you change the buffer size to the actual MTU of the interface.
- pub fn interface_mtu(&mut self) -> io::Result<usize> {
- let mut bufsize: libc::c_int = 1;
- try_ioctl!(self.fd, BIOCIMMEDIATE, &mut bufsize as *mut libc::c_int);
- try_ioctl!(self.fd, BIOCGBLEN, &mut bufsize as *mut libc::c_int);
- Ok(bufsize as usize)
- }
- pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
- unsafe {
- let len = libc::read(
- self.fd,
- buffer.as_mut_ptr() as *mut libc::c_void,
- buffer.len(),
- );
- if len == -1 || len < BPF_HDRLEN as isize {
- return Err(io::Error::last_os_error());
- }
- let len = len as usize;
- libc::memmove(
- buffer.as_mut_ptr() as *mut libc::c_void,
- &buffer[BPF_HDRLEN] as *const u8 as *const libc::c_void,
- len - BPF_HDRLEN,
- );
- Ok(len)
- }
- }
- pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
- unsafe {
- let len = libc::write(
- self.fd,
- buffer.as_ptr() as *const libc::c_void,
- buffer.len(),
- );
- if len == -1 {
- Err(io::Error::last_os_error()).unwrap()
- }
- Ok(len as usize)
- }
- }
- }
- impl Drop for BpfDevice {
- fn drop(&mut self) {
- unsafe {
- libc::close(self.fd);
- }
- }
- }
|