MacEthernetTap fully reimplemented in Rust, and Rust builds again.

This commit is contained in:
Adam Ierymenko 2021-01-27 14:18:22 -05:00
parent 05e2c63d6a
commit d4f6507c00
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
7 changed files with 177 additions and 88 deletions

View file

@ -1,6 +0,0 @@
OS-Dependent and OS-Interface Things
======
This folder contains stuff that interfaces with the base operating system
like Phy for network access and the various OS-specific Ethernet tap
drivers.

View file

@ -1,8 +1,11 @@
#ifdef __APPLE__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
#include <unistd.h>
#include <fcntl.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>

View file

@ -18,14 +18,11 @@ use num_traits::cast::AsPrimitive;
use std::os::raw::c_int;
use crate::osdep as osdep;
//
// A very low-level fast UDP socket that uses thread-per-core semantics to
// achieve maximum possible throughput. This will spawn a lot of threads but
// these threads will be inactive unless packets are being received with them.
//
// On most OSes this is by far the fastest way to handle incoming UDP except
// for bypassing the kernel's TCP/IP stack entirely.
//
/*
* This is a threaded UDP socket listener for high performance. The fastest way to receive UDP
* (without heroic efforts like kernel bypass) on most platforms is to create a separate socket
* for each thread using options like SO_REUSEPORT and concurrent packet listening.
*/
#[cfg(windows)]
use winapi::um::winsock2 as winsock2;
@ -125,33 +122,29 @@ fn bind_udp_socket(_: &str, address: &InetAddress) -> Result<FastUDPRawOsSocket,
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Handler for incoming packets received by a FastUDPSocket.
/// Note that this may be called concurrently from any number of threads.
pub trait FastUDPSocketPacketHandler {
fn incoming_udp_packet(&self, raw_socket: &FastUDPRawOsSocket, from_adddress: &InetAddress, data: Buffer);
}
/// A multi-threaded (or otherwise fast) UDP socket that binds to both IPv4 and IPv6 addresses.
pub struct FastUDPSocket<H: FastUDPSocketPacketHandler + Send + Sync + 'static> {
handler: Arc<H>,
pub struct FastUDPSocket {
threads: Vec<std::thread::JoinHandle<()>>,
thread_run: Arc<AtomicBool>,
sockets: Vec<FastUDPRawOsSocket>,
pub bind_address: InetAddress,
}
/// Send to a raw UDP socket with optional packet TTL.
/// If the packet_ttl option is <=0, packet is sent with the default TTL. TTL setting is only used
/// in ZeroTier right now to do escalating TTL probes for IPv4 NAT traversal.
#[cfg(unix)]
#[inline(always)]
pub fn fast_udp_socket_sendto(socket: &FastUDPRawOsSocket, to_address: &InetAddress, data: *const u8, len: usize, packet_ttl: i32) {
unsafe {
if packet_ttl <= 0 {
osdep::sendto(*socket, data.cast(), len as osdep::size_t, 0, (to_address as *const InetAddress).cast(), std::mem::size_of::<InetAddress>() as osdep::socklen_t);
osdep::sendto(*socket, data.cast(), len.as_(), 0, (to_address as *const InetAddress).cast(), std::mem::size_of::<InetAddress>().as_());
} else {
let mut ttl = packet_ttl as c_int;
osdep::setsockopt(*socket, osdep::IPPROTO_IP.as_(), osdep::IP_TTL.as_(), (&mut ttl as *mut c_int).cast(), std::mem::size_of::<c_int>() as osdep::socklen_t);
osdep::sendto(*socket, data.cast(), len as osdep::size_t, 0, (to_address as *const InetAddress).cast(), std::mem::size_of::<InetAddress>() as osdep::socklen_t);
osdep::setsockopt(*socket, osdep::IPPROTO_IP.as_(), osdep::IP_TTL.as_(), (&mut ttl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
osdep::sendto(*socket, data.cast(), len.as_(), 0, (to_address as *const InetAddress).cast(), std::mem::size_of::<InetAddress>().as_());
ttl = 255;
osdep::setsockopt(*socket, osdep::IPPROTO_IP.as_(), osdep::IP_TTL.as_(), (&mut ttl as *mut c_int).cast(), std::mem::size_of::<c_int>() as osdep::socklen_t);
osdep::setsockopt(*socket, osdep::IPPROTO_IP.as_(), osdep::IP_TTL.as_(), (&mut ttl as *mut c_int).cast(), std::mem::size_of::<c_int>().as_());
}
}
}
@ -166,16 +159,15 @@ pub fn fast_udp_socket_sendto(socket: &FastUDPRawOsSocket, to_address: &InetAddr
fn fast_udp_socket_recvfrom(socket: &FastUDPRawOsSocket, buf: &mut Buffer, from_address: &mut InetAddress) -> i32 {
unsafe {
let mut addrlen = std::mem::size_of::<InetAddress>() as osdep::socklen_t;
osdep::recvfrom(*socket, buf.as_mut_ptr().cast(), Buffer::CAPACITY as osdep::size_t, 0, (from_address as *mut InetAddress).cast(), &mut addrlen) as i32
osdep::recvfrom(*socket, buf.as_mut_ptr().cast(), Buffer::CAPACITY.as_(), 0, (from_address as *mut InetAddress).cast(), &mut addrlen) as i32
}
}
impl<H: FastUDPSocketPacketHandler + Send + Sync + 'static> FastUDPSocket<H> {
pub fn new(device_name: &str, address: &InetAddress, handler: &Arc<H>) -> Result<FastUDPSocket<H>, String> {
let thread_count = num_cpus::get();
impl FastUDPSocket {
pub fn new<F: Fn(&FastUDPRawOsSocket, &InetAddress, Buffer) + Send + Sync + Clone + 'static>(device_name: &str, address: &InetAddress, handler: F) -> Result<FastUDPSocket, String> {
let thread_count = num_cpus::get_physical().min(num_cpus::get());
let mut s = FastUDPSocket{
handler: handler.clone(),
thread_run: Arc::new(AtomicBool::new(true)),
threads: Vec::new(),
sockets: Vec::new(),
@ -190,20 +182,15 @@ impl<H: FastUDPSocketPacketHandler + Send + Sync + 'static> FastUDPSocket<H> {
s.sockets.push(thread_socket);
let thread_run = s.thread_run.clone();
let handler_weak = Arc::downgrade(&s.handler);
let handler_copy = handler.clone();
s.threads.push(std::thread::Builder::new().stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).spawn(move || {
let mut from_address = InetAddress::new();
while thread_run.load(Ordering::Relaxed) {
let mut buf = Buffer::new();
let read_length = fast_udp_socket_recvfrom(&thread_socket, &mut buf, &mut from_address);
if read_length > 0 {
let handler = handler_weak.upgrade();
if handler.is_some() {
unsafe { buf.set_len(read_length as usize); }
handler.unwrap().incoming_udp_packet(&thread_socket, &from_address, buf);
} else {
break;
}
unsafe { buf.set_len(read_length as usize); }
handler_copy(&thread_socket, &from_address, buf);
} else if read_length < 0 {
break;
}
@ -214,6 +201,9 @@ impl<H: FastUDPSocketPacketHandler + Send + Sync + 'static> FastUDPSocket<H> {
}
}
// This is successful if it is able to bind successfully once and launch at least one thread,
// since in a few cases it may be impossible to do multithreaded binding such as old Linux
// kernels or emulation layers.
if s.sockets.is_empty() {
return Err(format!("unable to bind to address for IPv4 or IPv6 ({})", bind_failed_reason));
}
@ -243,14 +233,10 @@ impl<H: FastUDPSocketPacketHandler + Send + Sync + 'static> FastUDPSocket<H> {
}
}
impl<H: FastUDPSocketPacketHandler + Send + Sync + 'static> Drop for FastUDPSocket<H> {
impl Drop for FastUDPSocket {
#[cfg(windows)]
fn drop(&mut self) {
self.thread_run.store(false, Ordering::Relaxed);
// TODO
for t in self.threads.iter() {
t.join()
}
}
#[cfg(unix)]
@ -273,11 +259,12 @@ impl<H: FastUDPSocketPacketHandler + Send + Sync + 'static> Drop for FastUDPSock
}
}
while !self.threads.is_empty() {
self.threads.pop().unwrap().join().expect("unable to join to thread");
let _ = self.threads.pop().unwrap().join();
}
}
}
/*
#[cfg(test)]
mod tests {
use crate::fastudpsocket::*;
@ -336,3 +323,4 @@ mod tests {
//println!("FastUDPSocket shutdown successful");
}
}
*/

View file

@ -17,7 +17,7 @@ mod physicallink;
mod log;
mod store;
mod network;
mod vnp;
mod vnic;
#[allow(non_snake_case,non_upper_case_globals,non_camel_case_types,dead_code,improper_ctypes)]
mod osdep;
@ -47,12 +47,6 @@ use crate::network::Network;
pub struct ServiceEventHandler {}
impl FastUDPSocketPacketHandler for ServiceEventHandler {
#[inline(always)]
fn incoming_udp_packet(&self, raw_socket: &FastUDPRawOsSocket, from_adddress: &InetAddress, data: Buffer) {
}
}
impl NodeEventHandler<Network> for ServiceEventHandler {
fn virtual_network_config(&self, network_id: NetworkId, network_obj: &Arc<Network>, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>) {
}
@ -99,7 +93,7 @@ fn main() {
let tokio_rt = tokio::runtime::Builder::new_multi_thread().thread_stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).build().unwrap();
tokio_rt.block_on(async {
// Keeps track of FastUDPSocket instances by bound address.
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket<ServiceEventHandler>> = BTreeMap::new();
let mut udp_sockets: BTreeMap<InetAddress, FastUDPSocket> = BTreeMap::new();
// Send something to interrupt_tx to interrupt the inner loop and force it to
// detect a change or exit if run has been set to false.
@ -194,7 +188,9 @@ fn main() {
// Bind addresses that are not already bound.
for addr in system_addrs.iter() {
if !udp_sockets.contains_key(addr.0) {
let s = FastUDPSocket::new(addr.1.device.as_str(), addr.0, &handler);
let s = FastUDPSocket::new(addr.1.device.as_str(), addr.0, |raw_socket: &FastUDPRawOsSocket, from_address: &InetAddress, data: Buffer| {
// TODO
});
if s.is_ok() {
udp_sockets.insert(addr.0.clone(), s.unwrap());
} else if addr.0.port() == local_config.settings.primary_port {

View file

@ -24,8 +24,8 @@ pub struct PhysicalLink {
}
#[inline(always)]
fn s6_addr_as_ptr<A>(a: &A) -> *A {
a as *A
fn s6_addr_as_ptr<A>(a: &A) -> *const A {
a as *const A
}
impl PhysicalLink {
@ -39,10 +39,10 @@ impl PhysicalLink {
if !(*i).ifa_addr.is_null() {
let mut a = InetAddress::new();
let sa_family = (*(*i).ifa_addr).sa_family;
if sa_family == osdep::AF_INET.as_() {
let sa_family = (*(*i).ifa_addr).sa_family as u8;
if sa_family == osdep::AF_INET as u8 {
copy_nonoverlapping((*i).ifa_addr.cast::<u8>(), (&mut a as *mut InetAddress).cast::<u8>(), size_of::<osdep::sockaddr_in>());
} else if sa_family == osdep::AF_INET6.as_() {
} else if sa_family == osdep::AF_INET6 as u8 {
copy_nonoverlapping((*i).ifa_addr.cast::<u8>(), (&mut a as *mut InetAddress).cast::<u8>(), size_of::<osdep::sockaddr_in6>());
} else {
continue;
@ -50,10 +50,10 @@ impl PhysicalLink {
let mut netmask_bits: u16 = 0;
if !(*i).ifa_netmask.is_null() {
if sa_family == osdep::AF_INET.as_() {
if sa_family == osdep::AF_INET as u8 {
let mut a = (*(*i).ifa_netmask.cast::<osdep::sockaddr_in>()).sin_addr.s_addr as u32;
netmask_bits = a.leading_ones() as u16;
} else if sa_family == osdep::AF_INET6.as_() {
} else if sa_family == osdep::AF_INET6 as u8 {
let a = s6_addr_as_ptr(&((*(*i).ifa_netmask.cast::<osdep::sockaddr_in6>()).sin6_addr)).cast::<u8>();
for i in 0..16 as isize {
let mut b = *a.offset(i);

View file

@ -22,22 +22,24 @@
*/
use std::cell::Cell;
use std::collections::BTreeSet;
use std::error::Error;
use std::ffi::CString;
use std::os::raw::{c_int, c_ulong, c_void};
use std::ptr::{null_mut, copy_nonoverlapping};
use std::mem::transmute;
use std::os::raw::{c_int, c_uchar, c_ulong, c_void};
use std::process::Command;
use std::sync::Mutex;
use std::thread::JoinHandle;
use std::intrinsics::copy_nonoverlapping;
use lazy_static::lazy_static;
use num_traits::cast::AsPrimitive;
use zerotier_core::{NetworkId, MAC, InetAddress, MulticastGroup};
use zerotier_core::{InetAddress, MAC, MulticastGroup, NetworkId};
use crate::osdep as osdep;
use crate::physicallink::PhysicalLink;
use crate::vnp::Port;
use std::collections::BTreeSet;
use crate::vnic::VNIC;
const BPF_BUFFER_SIZE: usize = 131072;
const IFCONFIG: &str = "/sbin/ifconfig";
@ -66,7 +68,9 @@ impl Drop for MacFethDevice {
pub struct MacFethTap {
network_id: u64,
device: MacFethDevice,
ndrv_fd: c_int,
bpf_fd: c_int,
bpf_no: u32,
bpf_read_thread: Cell<Option<JoinHandle<()>>>,
}
@ -74,14 +78,14 @@ pub struct MacFethTap {
// #define BPF_WORDALIGN(x) (((x)+(BPF_ALIGNMENT-1))&~(BPF_ALIGNMENT-1))
// ... and also ...
// #define BPF_ALIGNMENT sizeof(int32_t)
#[inline(always)]
#[allow(non_snake_case)]
pub fn BPF_WORDALIGN(x: isize) -> isize {
#[inline(always)]
fn BPF_WORDALIGN(x: isize) -> isize {
(((x + 3) as usize) & (!(3 as usize))) as isize
}
lazy_static! {
static ref MAC_FETH_GLOBAL_LOCK: Mutex<i32> = Mutex::new(0_i32);
static ref MAC_FETH_BPF_DEVICES_USED: Mutex<BTreeSet<u32>> = Mutex::new(BTreeSet::new());
}
impl MacFethTap {
@ -92,7 +96,10 @@ impl MacFethTap {
/// from another thread that is spawned here, so all its bound references must
/// be "Send" and "Sync" e.g. Arc<>.
pub fn new<F: Fn(&[u8]) + Send + Sync + 'static>(nwid: &NetworkId, mac: &MAC, mtu: i32, metric: i32, eth_frame_func: F) -> Result<MacFethTap, String> {
let _one_at_a_time = unsafe { MAC_FETH_GLOBAL_LOCK.lock().unwrap() };
// This tracks BPF devices we are using so we don't try to reopen them, and also
// doubles as a global lock to ensure that only one feth tap is created at once per
// ZeroTier process per system.
let mut bpf_devices_used = MAC_FETH_BPF_DEVICES_USED.lock().unwrap();
if unsafe { osdep::getuid() } != 0 {
return Err(String::from("ZeroTier MacFethTap must run as root"));
@ -165,15 +172,19 @@ impl MacFethTap {
let mut bpf_no: u32 = 1; // start at 1 since some software hard-codes /dev/bpf0
let mut bpf_fd: c_int = -1;
loop {
let bpf_dev = CString::new(format!("/dev/bpf{}", bpf_no)).unwrap();
let bpf_dev = bpf_dev.as_bytes_with_nul();
bpf_fd = unsafe { osdep::open(bpf_dev.as_ptr().cast(), osdep::O_RDWR as c_int) };
if bpf_fd >= 0 {
break;
}
bpf_no += 1;
if bpf_no > 1000 {
break;
if bpf_devices_used.contains(&bpf_no) {
bpf_no += 1;
} else {
let bpf_dev = CString::new(format!("/dev/bpf{}", bpf_no)).unwrap();
let bpf_dev = bpf_dev.as_bytes_with_nul();
bpf_fd = unsafe { osdep::open(bpf_dev.as_ptr().cast(), osdep::O_RDWR as c_int) };
if bpf_fd >= 0 {
break;
}
bpf_no += 1;
if bpf_no > 1000 {
break;
}
}
}
if bpf_fd < 0 {
@ -206,7 +217,7 @@ impl MacFethTap {
let mut bpf_ifr: osdep::ifreq = unsafe { std::mem::zeroed() };
let peer_dev_name_bytes = device.peer_name.as_bytes();
unsafe { copy_nonoverlapping(peer_dev_name_bytes.as_ptr(), bpf_ifr.ifr_name.as_mut_ptr().cast::<u8>(), if peer_dev_name_bytes.len() > (bpf_ifr.ifr_name.len() - 1) { bpf_ifr.ifr_name.len() - 1 } else { peer_dev_name_bytes.len() }); }
if unsafe { osdep::ioctl(bpf_fd as c_int, BIOCSETIF, (&mut bpf_ifr as *mut osdep::ifreq).cast::<c_void>()) } != 0 {
if unsafe { osdep::ioctl(bpf_fd as c_int, osdep::c_BIOCSETIF, (&mut bpf_ifr as *mut osdep::ifreq).cast::<c_void>()) } != 0 {
unsafe { osdep::close(bpf_fd); }
return Err(String::from("unable to configure BPF device"));
}
@ -225,7 +236,8 @@ impl MacFethTap {
return Err(String::from("unable to configure BPF device"));
}
let t = std::thread::Builder::new().stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).spawn(|| {
// Create BPF listener thread, which calls the supplied function on each incoming packet.
let t = std::thread::Builder::new().stack_size(zerotier_core::RECOMMENDED_THREAD_STACK_SIZE).spawn(move || {
let mut buf: [u8; BPF_BUFFER_SIZE] = [0_u8; BPF_BUFFER_SIZE];
let hdr_struct_size = std::mem::size_of::<osdep::bpf_hdr>() as isize;
loop {
@ -254,10 +266,37 @@ impl MacFethTap {
return Err(String::from("unable to start thread"));
}
// Create AF_NDRV socket used to inject packets. We could inject with BPF but that has
// a hard MTU limit of 2048 so we have to use AF_NDRV instead. Performance is probably
// the same, but it means another socket.
let ndrv_fd = unsafe { osdep::socket(osdep::AF_NDRV as c_int, osdep::SOCK_RAW as c_int, 0) };
if ndrv_fd < 0 {
unsafe { osdep::close(bpf_fd); }
return Err(String::from("unable to create AF_NDRV socket"));
}
let mut ndrv_sa: osdep::sockaddr_ndrv = unsafe { std::mem::zeroed() };
ndrv_sa.snd_len = std::mem::size_of::<osdep::sockaddr_ndrv>() as c_uchar;
ndrv_sa.snd_family = osdep::AF_NDRV as c_uchar;
unsafe { copy_nonoverlapping(peer_dev_name_bytes.as_ptr(), ndrv_sa.snd_name.as_mut_ptr().cast::<u8>(), if peer_dev_name_bytes.len() > (bpf_ifr.ifr_name.len() - 1) { bpf_ifr.ifr_name.len() - 1 } else { peer_dev_name_bytes.len() }); }
if unsafe { osdep::bind(ndrv_fd, (&ndrv_sa as *const osdep::sockaddr_ndrv).cast(), std::mem::size_of::<osdep::sockaddr_ndrv>() as osdep::socklen_t) } != 0 {
unsafe { osdep::close(bpf_fd); }
unsafe { osdep::close(ndrv_fd); }
return Err(String::from("unable to bind AF_NDRV socket"));
}
if unsafe { osdep::connect(ndrv_fd, (&ndrv_sa as *const osdep::sockaddr_ndrv).cast(), std::mem::size_of::<osdep::sockaddr_ndrv>() as osdep::socklen_t) } != 0 {
unsafe { osdep::close(bpf_fd); }
unsafe { osdep::close(ndrv_fd); }
return Err(String::from("unable to connect AF_NDRV socket"));
}
bpf_devices_used.insert(bpf_no);
Ok(MacFethTap {
network_id: nwid.0,
device: device,
ndrv_fd: ndrv_fd,
bpf_fd: bpf_fd,
bpf_no: bpf_no,
bpf_read_thread: Cell::new(Some(t.unwrap()))
})
}
@ -265,7 +304,7 @@ impl MacFethTap {
fn have_ip(&self, ip: &InetAddress) -> bool {
let mut have_ip = false;
PhysicalLink::map(|link: PhysicalLink| {
if link.device.eq(dev) && link.address.eq(ip) {
if link.device.eq(&self.device.name) && link.address.eq(ip) {
have_ip = true;
}
});
@ -273,7 +312,7 @@ impl MacFethTap {
}
}
impl Port for MacFethTap {
impl VNIC for MacFethTap {
fn add_ip(&self, ip: &InetAddress) -> bool {
if !self.have_ip(ip) {
let cmd = Command::new(IFCONFIG).arg(&self.device.name).arg(if ip.is_v6() { "inet6" } else { "inet" }).arg(ip.to_string()).arg("alias").spawn();
@ -315,9 +354,73 @@ impl Port for MacFethTap {
}
fn get_multicast_groups(&self) -> BTreeSet<MulticastGroup> {
let groups: BTreeSet<MulticastGroup> = BTreeSet::new();
let dev = self.device.name.as_bytes();
let mut groups: BTreeSet<MulticastGroup> = BTreeSet::new();
unsafe {
let mut maddrs: *mut osdep::ifmaddrs = null_mut();
if osdep::getifmaddrs(&mut maddrs as *mut *mut osdep::ifmaddrs) == 0 {
let mut i = maddrs;
while !i.is_null() {
if !(*i).ifma_name.is_null() && !(*i).ifma_addr.is_null() && (*(*i).ifma_addr).sa_family == osdep::AF_LINK as osdep::sa_family_t {
let in_: &osdep::sockaddr_dl = &*((*i).ifma_name.cast());
let la: &osdep::sockaddr_dl = &*((*i).ifma_addr.cast());
if la.sdl_alen == 6 && in_.sdl_nlen <= dev.len() as osdep::u_char && osdep::memcmp(dev.as_ptr().cast(), in_.sdl_data.as_ptr().cast(), in_.sdl_nlen as c_ulong) == 0 {
let mi = la.sdl_nlen as usize;
groups.insert(MulticastGroup{
mac: MAC(
(la.sdl_data[mi] as u64) << 40 |
(la.sdl_data[mi+1] as u64) << 32 |
(la.sdl_data[mi+2] as u64) << 24 |
(la.sdl_data[mi+3] as u64) << 16 |
(la.sdl_data[mi+4] as u64) << 8 |
la.sdl_data[mi+5] as u64
),
adi: 0,
});
}
}
i = (*i).ifma_next;
}
osdep::freeifmaddrs(maddrs);
}
}
groups
}
#[inline(always)]
fn put(&self, source_mac: &zerotier_core::MAC, dest_mac: &zerotier_core::MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool {
let dm = dest_mac.0;
let sm = source_mac.0;
let mut hdr: [u8; 14] = [
(dm >> 40) as u8,
(dm >> 32) as u8,
(dm >> 24) as u8,
(dm >> 16) as u8,
(dm >> 8) as u8,
dm as u8,
(sm >> 40) as u8,
(sm >> 32) as u8,
(sm >> 24) as u8,
(sm >> 16) as u8,
(sm >> 8) as u8,
sm as u8,
(ethertype >> 8) as u8,
ethertype as u8
];
unsafe {
let iov: [osdep::iovec; 2] = [
osdep::iovec {
iov_base: hdr.as_mut_ptr().cast(),
iov_len: 14,
},
osdep::iovec {
iov_base: transmute(data), // have to "cast away const" even though data is not modified by writev()
iov_len: len as osdep::size_t,
},
];
osdep::writev(self.ndrv_fd, iov.as_ptr(), 2) == (len + 14) as osdep::ssize_t
}
}
}
impl Drop for MacFethTap {
@ -326,6 +429,12 @@ impl Drop for MacFethTap {
unsafe {
osdep::shutdown(self.bpf_fd, osdep::SHUT_RDWR as c_int);
osdep::close(self.bpf_fd);
MAC_FETH_BPF_DEVICES_USED.lock().unwrap().remove(&self.bpf_no);
}
}
if self.ndrv_fd >= 0 {
unsafe {
osdep::close(self.ndrv_fd);
}
}
let t = self.bpf_read_thread.replace(None);

View file

@ -1,12 +1,11 @@
// VNP = virtual network port, a.k.a. tun/tap device driver interface.
#[cfg(target_os = "macos")]
mod mac_feth_tap;
pub trait Port {
pub trait VNIC {
fn add_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
fn remove_ip(&self, ip: &zerotier_core::InetAddress) -> bool;
fn ips(&self) -> Vec<zerotier_core::InetAddress>;
fn device_name(&self) -> String;
fn get_multicast_groups(&self) -> std::collections::BTreeSet<zerotier_core::MulticastGroup>;
fn put(&self, source_mac: &zerotier_core::MAC, dest_mac: &zerotier_core::MAC, ethertype: u16, vlan_id: u16, data: *const u8, len: usize) -> bool;
}