diff --git a/Cargo.lock b/Cargo.lock index 9e5b30d1af42..ec64baf1138d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2740,9 +2740,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pcap" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f1686828a29fd8002fbf9c01506b4b2dd575c2305e1b884da3731abae8b9e0" +checksum = "fe4d339439e5e7f8ce32d58c2b58d5e304790e66f3aa0bd391dd6a9dc676e054" dependencies = [ "bitflags 1.3.2", "errno 0.2.8", diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index be8d6ca39b20..a5a25aad5934 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -53,7 +53,7 @@ system-configuration = "0.5.1" hickory-proto = "0.24.1" hickory-server = { version = "0.24.1", features = ["resolver"] } talpid-platform-metadata = { path = "../talpid-platform-metadata" } -pcap = { version = "2.0", features = ["capture-stream"] } +pcap = { version = "2.1", features = ["capture-stream"] } pnet_packet = "0.34" tun = { version = "0.5.5", features = ["async"] } nix = { version = "0.28", features = ["socket"] } diff --git a/talpid-core/src/split_tunnel/macos/bindings.rs b/talpid-core/src/split_tunnel/macos/bindings.rs index 931ac84d2e97..425527d5c01b 100644 --- a/talpid-core/src/split_tunnel/macos/bindings.rs +++ b/talpid-core/src/split_tunnel/macos/bindings.rs @@ -1,7 +1,6 @@ -// automatically generated by rust-bindgen 0.69.2 +/* automatically generated by rust-bindgen 0.70.1 */ pub const PTH_FLAG_DIR_OUT: u32 = 2; -pub const PCAP_ERRBUF_SIZE: u32 = 256; pub type __int32_t = ::std::os::raw::c_int; pub type __darwin_pid_t = __int32_t; pub type __darwin_uuid_t = [::std::os::raw::c_uchar; 16usize]; @@ -12,41 +11,13 @@ pub struct timeval32 { pub tv_sec: __int32_t, pub tv_usec: __int32_t, } -#[test] -fn bindgen_test_layout_timeval32() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 8usize, - concat!("Size of: ", stringify!(timeval32)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(timeval32)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tv_sec) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(timeval32), - "::", - stringify!(tv_sec) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).tv_usec) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(timeval32), - "::", - stringify!(tv_usec) - ) - ); -} +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of timeval32"][::std::mem::size_of::() - 8usize]; + ["Alignment of timeval32"][::std::mem::align_of::() - 4usize]; + ["Offset of field: timeval32::tv_sec"][::std::mem::offset_of!(timeval32, tv_sec) - 0usize]; + ["Offset of field: timeval32::tv_usec"][::std::mem::offset_of!(timeval32, tv_usec) - 4usize]; +}; pub type uuid_t = __darwin_uuid_t; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -72,237 +43,48 @@ pub struct pktap_header { pub pth_uuid: uuid_t, pub pth_euuid: uuid_t, } -#[test] -fn bindgen_test_layout_pktap_header() { - const UNINIT: ::std::mem::MaybeUninit = ::std::mem::MaybeUninit::uninit(); - let ptr = UNINIT.as_ptr(); - assert_eq!( - ::std::mem::size_of::(), - 156usize, - concat!("Size of: ", stringify!(pktap_header)) - ); - assert_eq!( - ::std::mem::align_of::(), - 4usize, - concat!("Alignment of ", stringify!(pktap_header)) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_length) as usize - ptr as usize }, - 0usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_type_next) as usize - ptr as usize }, - 4usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_type_next) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_dlt) as usize - ptr as usize }, - 8usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_dlt) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_ifname) as usize - ptr as usize }, - 12usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_ifname) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_flags) as usize - ptr as usize }, - 36usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_flags) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_protocol_family) as usize - ptr as usize }, - 40usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_protocol_family) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_frame_pre_length) as usize - ptr as usize }, - 44usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_frame_pre_length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_frame_post_length) as usize - ptr as usize }, - 48usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_frame_post_length) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_pid) as usize - ptr as usize }, - 52usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_pid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_comm) as usize - ptr as usize }, - 56usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_comm) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_svc) as usize - ptr as usize }, - 76usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_svc) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_iftype) as usize - ptr as usize }, - 80usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_iftype) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_ifunit) as usize - ptr as usize }, - 82usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_ifunit) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_epid) as usize - ptr as usize }, - 84usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_epid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_ecomm) as usize - ptr as usize }, - 88usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_ecomm) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_flowid) as usize - ptr as usize }, - 108usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_flowid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_ipproto) as usize - ptr as usize }, - 112usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_ipproto) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_tstamp) as usize - ptr as usize }, - 116usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_tstamp) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_uuid) as usize - ptr as usize }, - 124usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_uuid) - ) - ); - assert_eq!( - unsafe { ::std::ptr::addr_of!((*ptr).pth_euuid) as usize - ptr as usize }, - 140usize, - concat!( - "Offset of field: ", - stringify!(pktap_header), - "::", - stringify!(pth_euuid) - ) - ); -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct pcap { - _unused: [u8; 0], -} -pub type pcap_t = pcap; -extern "C" { - pub fn pcap_create( - arg1: *const ::std::os::raw::c_char, - arg2: *mut ::std::os::raw::c_char, - ) -> *mut pcap_t; -} -extern "C" { - pub fn pcap_set_want_pktap( - arg1: *mut pcap_t, - arg2: ::std::os::raw::c_int, - ) -> ::std::os::raw::c_int; -} -pub const BIOCSWANTPKTAP: u64 = 3221504639; +#[allow(clippy::unnecessary_operation, clippy::identity_op)] +const _: () = { + ["Size of pktap_header"][::std::mem::size_of::() - 156usize]; + ["Alignment of pktap_header"][::std::mem::align_of::() - 4usize]; + ["Offset of field: pktap_header::pth_length"] + [::std::mem::offset_of!(pktap_header, pth_length) - 0usize]; + ["Offset of field: pktap_header::pth_type_next"] + [::std::mem::offset_of!(pktap_header, pth_type_next) - 4usize]; + ["Offset of field: pktap_header::pth_dlt"] + [::std::mem::offset_of!(pktap_header, pth_dlt) - 8usize]; + ["Offset of field: pktap_header::pth_ifname"] + [::std::mem::offset_of!(pktap_header, pth_ifname) - 12usize]; + ["Offset of field: pktap_header::pth_flags"] + [::std::mem::offset_of!(pktap_header, pth_flags) - 36usize]; + ["Offset of field: pktap_header::pth_protocol_family"] + [::std::mem::offset_of!(pktap_header, pth_protocol_family) - 40usize]; + ["Offset of field: pktap_header::pth_frame_pre_length"] + [::std::mem::offset_of!(pktap_header, pth_frame_pre_length) - 44usize]; + ["Offset of field: pktap_header::pth_frame_post_length"] + [::std::mem::offset_of!(pktap_header, pth_frame_post_length) - 48usize]; + ["Offset of field: pktap_header::pth_pid"] + [::std::mem::offset_of!(pktap_header, pth_pid) - 52usize]; + ["Offset of field: pktap_header::pth_comm"] + [::std::mem::offset_of!(pktap_header, pth_comm) - 56usize]; + ["Offset of field: pktap_header::pth_svc"] + [::std::mem::offset_of!(pktap_header, pth_svc) - 76usize]; + ["Offset of field: pktap_header::pth_iftype"] + [::std::mem::offset_of!(pktap_header, pth_iftype) - 80usize]; + ["Offset of field: pktap_header::pth_ifunit"] + [::std::mem::offset_of!(pktap_header, pth_ifunit) - 82usize]; + ["Offset of field: pktap_header::pth_epid"] + [::std::mem::offset_of!(pktap_header, pth_epid) - 84usize]; + ["Offset of field: pktap_header::pth_ecomm"] + [::std::mem::offset_of!(pktap_header, pth_ecomm) - 88usize]; + ["Offset of field: pktap_header::pth_flowid"] + [::std::mem::offset_of!(pktap_header, pth_flowid) - 108usize]; + ["Offset of field: pktap_header::pth_ipproto"] + [::std::mem::offset_of!(pktap_header, pth_ipproto) - 112usize]; + ["Offset of field: pktap_header::pth_tstamp"] + [::std::mem::offset_of!(pktap_header, pth_tstamp) - 116usize]; + ["Offset of field: pktap_header::pth_uuid"] + [::std::mem::offset_of!(pktap_header, pth_uuid) - 124usize]; + ["Offset of field: pktap_header::pth_euuid"] + [::std::mem::offset_of!(pktap_header, pth_euuid) - 140usize]; +}; diff --git a/talpid-core/src/split_tunnel/macos/bpf.rs b/talpid-core/src/split_tunnel/macos/bpf.rs index ec3f8fc62d58..566cc1f56b50 100644 --- a/talpid-core/src/split_tunnel/macos/bpf.rs +++ b/talpid-core/src/split_tunnel/macos/bpf.rs @@ -22,8 +22,6 @@ use std::{ }; use tokio::io::{unix::AsyncFd, AsyncRead, Interest, ReadBuf}; -use super::bindings::BIOCSWANTPKTAP; - #[derive(thiserror::Error, Debug)] pub enum Error { /// Failed to open BPF device @@ -167,12 +165,6 @@ impl Bpf { unsafe { ioctl!(self.file.as_raw_fd(), BIOCSHDRCMPLT, &enable) } } - pub fn set_want_pktap(&self, enable: bool) -> Result<(), Error> { - let enable: c_int = if enable { 1 } else { 0 }; - // SAFETY: The fd is valid for the lifetime of `self` - unsafe { ioctl!(self.file.as_raw_fd(), BIOCSWANTPKTAP, &enable) } - } - pub fn set_buffer_size(&self, mut buffer_size: c_uint) -> Result { // SAFETY: The fd is valid for the lifetime of `self` unsafe { diff --git a/talpid-core/src/split_tunnel/macos/generate-bindings.sh b/talpid-core/src/split_tunnel/macos/generate-bindings.sh index 9cfa5c1b625b..a52c09fd8619 100755 --- a/talpid-core/src/split_tunnel/macos/generate-bindings.sh +++ b/talpid-core/src/split_tunnel/macos/generate-bindings.sh @@ -13,9 +13,5 @@ curl https://opensource.apple.com/source/libpcap/libpcap-67/libpcap/pcap/pcap.h curl https://opensource.apple.com/source/xnu/xnu-3789.41.3/bsd/net/bpf.h -o include/bpf.h bindgen "include/bindings.h" -o ./bindings.rs \ - --allowlist-item "^pcap_create" \ - --allowlist-item "^pcap_set_want_pktap" \ --allowlist-item "^pktap_header" \ - --allowlist-item "PCAP_ERRBUF_SIZE" \ - --allowlist-item "^BIOCSWANTPKTAP" \ --allowlist-item "^PTH_FLAG_DIR_OUT" diff --git a/talpid-core/src/split_tunnel/macos/tun.rs b/talpid-core/src/split_tunnel/macos/tun.rs index f5cc58878630..0e260a151762 100644 --- a/talpid-core/src/split_tunnel/macos/tun.rs +++ b/talpid-core/src/split_tunnel/macos/tun.rs @@ -2,9 +2,7 @@ //! either the default interface or a VPN tunnel interface. use super::{ - bindings::{ - pcap_create, pcap_set_want_pktap, pktap_header, PCAP_ERRBUF_SIZE, PTH_FLAG_DIR_OUT, - }, + bindings::{pktap_header, PTH_FLAG_DIR_OUT}, bpf, default::DefaultInterface, }; @@ -25,7 +23,6 @@ use std::{ ffi::{c_uint, CStr}, io::{self, IoSlice, Write}, net::{Ipv4Addr, Ipv6Addr}, - ptr::NonNull, }; use talpid_routing::RouteManagerHandle; use tokio::{ @@ -57,7 +54,7 @@ pub enum Error { EnableNonblock(#[source] pcap::Error), /// pcap_create failed #[error("pcap_create failed: {}", _0)] - CreatePcap(String), + CreatePcap(#[source] pcap::Error), /// Failed to create packet stream #[error("Failed to create packet stream")] CreateStream(#[source] pcap::Error), @@ -806,8 +803,13 @@ fn fix_ipv6_checksums( fn capture_outbound_packets( utun_iface: &str, ) -> Result> + Send, Error> { - let cap = pktap_capture()? + // We want to create a pktap "pseudo-device" and capture data on it using a bpf device. + // This provides packet data plus a pktap header including process information. + // libpcap will do the heavy lifting for us if we simply request a "pktap" device. + let cap = pcap::Capture::from_device("pktap") + .map_err(Error::CreatePcap)? .immediate_mode(true) + .want_pktap(true) .open() .map_err(Error::CaptureSplitTunnelDevice)?; @@ -894,31 +896,3 @@ impl PacketCodec for PktapCodec { }) } } - -/// Create a pktap interface using `libpcap` -fn pktap_capture() -> Result, Error> { - // We want to create a pktap "pseudo-device" and capture data on it using a bpf device. - // This provides packet data plus a pktap header including process information. - // libpcap will do the heavy lifting for us if we simply request a "pktap" device. - - let mut errbuf = [0u8; PCAP_ERRBUF_SIZE as usize]; - - let pcap = unsafe { pcap_create(c"pktap".as_ptr(), errbuf.as_mut_ptr() as _) }; - if pcap.is_null() { - let errstr = CStr::from_bytes_until_nul(&errbuf) - .unwrap() - .to_string_lossy() - .into_owned(); - return Err(Error::CreatePcap(errstr)); - } - unsafe { pcap_set_want_pktap(pcap, 1) }; - - // TODO: Upstream setting "want pktap" directly on Capture - // If we had that, we could have simply used pcap::Capture::from_device("pktap") - // TODO: Also upstream exposure of a raw handle to pcap_t on Capture - - // just casting a pointer to a private type using _. that's fine, apparently - Ok(pcap::Capture::from(unsafe { - NonNull::new_unchecked(pcap as *mut _) - })) -}