mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
MacEthernetTap fully reimplemented in Rust, and Rust builds again.
This commit is contained in:
parent
05e2c63d6a
commit
d4f6507c00
7 changed files with 177 additions and 88 deletions
|
@ -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.
|
|
@ -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>
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
Loading…
Add table
Reference in a new issue