Skip to content

Commit

Permalink
macos: add ipv6 addr
Browse files Browse the repository at this point in the history
  • Loading branch information
pronebird committed Mar 4, 2024
1 parent e762c41 commit 13ae6ce
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 8 deletions.
35 changes: 31 additions & 4 deletions src/platform/macos/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,19 @@ use crate::{
error::*,
platform::{
macos::sys::*,
posix::{self, Fd, SockAddr},
posix::{self, Fd, SockAddr, SockAddrV6},
},
};
use libc::{
self, c_char, c_short, c_uint, c_void, sockaddr, socklen_t, AF_INET, AF_SYSTEM, AF_SYS_CONTROL,
IFF_RUNNING, IFF_UP, IFNAMSIZ, PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL, UTUN_OPT_IFNAME,
self, c_char, c_short, c_uint, c_void, sockaddr, socklen_t, AF_INET, AF_INET6, AF_SYSTEM,
AF_SYS_CONTROL, IFF_RUNNING, IFF_UP, IFNAMSIZ, PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL,
UTUN_OPT_IFNAME,
};
use std::{
ffi::CStr,
io::{self, Read, Write},
mem,
net::Ipv4Addr,
net::{Ipv4Addr, Ipv6Addr},
os::unix::io::{AsRawFd, IntoRawFd, RawFd},
ptr,
sync::Arc,
Expand All @@ -41,6 +42,7 @@ pub struct Device {
name: String,
queue: Queue,
ctl: Fd,
ctl_inet6: Fd,
}

impl Device {
Expand Down Expand Up @@ -111,13 +113,15 @@ impl Device {
}

let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))?;
let ctl_inet6 = Fd::new(libc::socket(AF_INET6, SOCK_DGRAM, 0))?;

Device {
name: CStr::from_ptr(name.as_ptr() as *const c_char)
.to_string_lossy()
.into(),
queue: Queue { tun },
ctl,
ctl_inet6,
}
};

Expand Down Expand Up @@ -166,6 +170,29 @@ impl Device {
}
}

/// Add IPv6 alias of the device.
/// Tun devices are permitted to have multiple IPv6 aliases.
pub fn add_ipv6_alias(&mut self, addr: Ipv6Addr, mask: Ipv6Addr) -> Result<()> {
unsafe {
let mut req: in6_aliasreq = mem::zeroed();
ptr::copy_nonoverlapping(
self.name.as_ptr() as *const c_char,
req.name.as_mut_ptr(),
self.name.len(),
);
req.addr = SockAddrV6::from(addr).into();
req.prefixmask = SockAddrV6::from(mask).into();
req.lifetime.ia6t_pltime = u32::MAX;
req.lifetime.ia6t_vltime = u32::MAX;

if siocaifaddr_in6(self.ctl_inet6.as_raw_fd(), &req) < 0 {
return Err(io::Error::last_os_error().into());
}

Ok(())
}
}

/// Split the interface into a `Reader` and `Writer`.
pub fn split(self) -> (posix::Reader, posix::Writer) {
let fd = Arc::new(self.queue.tun);
Expand Down
28 changes: 27 additions & 1 deletion src/platform/macos/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
//! Bindings to internal macOS stuff.
use ioctl::*;
use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void, sockaddr, IFNAMSIZ};
use libc::{
c_char, c_int, c_short, c_uint, c_ushort, c_void, sockaddr, sockaddr_in6, time_t, IFNAMSIZ,
};

pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";

Expand Down Expand Up @@ -109,6 +111,28 @@ pub struct ifaliasreq {
pub mask: sockaddr,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct in6_aliasreq {
pub name: [c_char; IFNAMSIZ],
pub addr: sockaddr_in6,
pub dstaddr: sockaddr_in6,
pub prefixmask: sockaddr_in6,
pub flags: c_int,
pub lifetime: in6_addrlifetime,
}

#[allow(non_camel_case_types)]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct in6_addrlifetime {
pub ia6t_expire: time_t,
pub ia6t_preferred: time_t,
pub ia6t_vltime: u32,
pub ia6t_pltime: u32,
}

ioctl!(readwrite ctliocginfo with 'N', 3; ctl_info);

ioctl!(write siocsifflags with 'i', 16; ifreq);
Expand All @@ -131,3 +155,5 @@ ioctl!(readwrite siocgifmtu with 'i', 51; ifreq);

ioctl!(write siocaifaddr with 'i', 26; ifaliasreq);
ioctl!(write siocdifaddr with 'i', 25; ifreq);

ioctl!(write siocaifaddr_in6 with 'i', 26; in6_aliasreq);
2 changes: 1 addition & 1 deletion src/platform/posix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
//! POSIX compliant support.
mod sockaddr;
pub use self::sockaddr::SockAddr;
pub use self::sockaddr::{SockAddr, SockAddrV6};

mod fd;
pub use self::fd::Fd;
Expand Down
62 changes: 60 additions & 2 deletions src/platform/posix/sockaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@
//
// 0. You just DO WHAT THE FUCK YOU WANT TO.

use libc::{in_addr, sockaddr, sockaddr_in};
use std::{mem, net::Ipv4Addr, ptr};
use libc::{in6_addr, in_addr, sockaddr, sockaddr_in, sockaddr_in6};
use std::{
mem,
net::{Ipv4Addr, Ipv6Addr},
ptr,
};

use crate::error::*;

Expand Down Expand Up @@ -79,3 +83,57 @@ impl From<SockAddr> for sockaddr_in {
addr.0
}
}

/// A wrapper for `sockaddr_in6`.
#[derive(Copy, Clone)]
pub struct SockAddrV6(sockaddr_in6);

impl SockAddrV6 {
/// Create a new `SockAddrV6` from a generic `sockaddr`.
pub fn new(value: &sockaddr_in6) -> Result<Self> {
if value.sin6_family != libc::AF_INET6 as libc::sa_family_t {
return Err(Error::InvalidAddress);
}

unsafe { Self::unchecked(value) }
}

/// # Safety
/// Create a new `SockAddrV6` and not check the source.
pub unsafe fn unchecked(value: &sockaddr_in6) -> Result<Self> {
Ok(SockAddrV6(ptr::read(value as *const _ as *const _)))
}
}

impl From<Ipv6Addr> for SockAddrV6 {
fn from(ip: Ipv6Addr) -> SockAddrV6 {
let mut addr = unsafe { mem::zeroed::<sockaddr_in6>() };
let addr_family = if ip.is_unspecified() {
libc::AF_UNSPEC
} else {
libc::AF_INET6
};

// macos: ioctl does not accept sockaddr_in6 without sin6_len set.
addr.sin6_len = mem::size_of::<sockaddr_in6>() as u8;
addr.sin6_family = addr_family as libc::sa_family_t;
addr.sin6_port = 0;
addr.sin6_addr = in6_addr {
s6_addr: ip.octets(),
};

SockAddrV6(addr)
}
}

impl From<SockAddrV6> for Ipv6Addr {
fn from(addr: SockAddrV6) -> Ipv6Addr {
Ipv6Addr::from(addr.0.sin6_addr.s6_addr)
}
}

impl From<SockAddrV6> for sockaddr_in6 {
fn from(addr: SockAddrV6) -> sockaddr_in6 {
addr.0
}
}

0 comments on commit 13ae6ce

Please sign in to comment.