use std::io;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;
use crate::platform::{DeviceImpl, SyncDevice};
#[derive(Clone, Copy, Default, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Layer {
#[cfg(any(
target_os = "windows",
target_os = "linux",
target_os = "freebsd",
target_os = "macos",
target_os = "openbsd",
target_os = "netbsd",
))]
L2,
#[default]
L3,
}
#[derive(Clone, Default, Debug)]
pub(crate) struct DeviceConfig {
pub(crate) dev_name: Option<String>,
#[cfg(windows)]
pub(crate) description: Option<String>,
#[cfg(target_os = "macos")]
pub(crate) peer_feth: Option<String>,
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub(crate) associate_route: Option<bool>,
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
pub(crate) reuse_dev: Option<bool>,
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub(crate) persist: Option<bool>,
#[allow(dead_code)]
pub(crate) layer: Option<Layer>,
#[cfg(windows)]
pub(crate) device_guid: Option<u128>,
#[cfg(windows)]
pub(crate) wintun_log: Option<bool>,
#[cfg(windows)]
pub(crate) wintun_file: Option<String>,
#[cfg(windows)]
pub(crate) ring_capacity: Option<u32>,
#[cfg(windows)]
pub(crate) delete_driver: Option<bool>,
#[cfg(windows)]
pub(crate) mac_address: Option<String>,
#[cfg(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub(crate) packet_information: Option<bool>,
#[cfg(target_os = "linux")]
pub(crate) offload: Option<bool>,
#[cfg(target_os = "linux")]
pub(crate) multi_queue: Option<bool>,
}
type IPV4 = (
io::Result<Ipv4Addr>,
io::Result<u8>,
Option<io::Result<Ipv4Addr>>,
);
#[doc(hidden)]
pub struct DeviceBuilderGuard<'a>(&'a mut DeviceBuilder);
#[doc(hidden)]
impl DeviceBuilderGuard<'_> {
#[cfg(windows)]
pub fn description<S: Into<String>>(&mut self, description: S) -> &mut Self {
self.0.description = Some(description.into());
self
}
#[cfg(windows)]
pub fn mtu_v4(&mut self, mtu: u16) -> &mut Self {
self.0.mtu = Some(mtu);
self
}
#[cfg(windows)]
pub fn mtu_v6(&mut self, mtu: u16) -> &mut Self {
self.0.mtu_v6 = Some(mtu);
self
}
#[cfg(any(
target_os = "windows",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "netbsd"
))]
pub fn mac_addr(&mut self, mac_addr: [u8; 6]) -> &mut Self {
self.0.mac_addr = Some(mac_addr);
self
}
#[cfg(windows)]
pub fn device_guid(&mut self, device_guid: u128) -> &mut Self {
self.0.device_guid = Some(device_guid);
self
}
#[cfg(windows)]
pub fn wintun_log(&mut self, wintun_log: bool) -> &mut Self {
self.0.wintun_log = Some(wintun_log);
self
}
#[cfg(windows)]
pub fn wintun_file(&mut self, wintun_file: String) -> &mut Self {
self.0.wintun_file = Some(wintun_file);
self
}
#[cfg(windows)]
pub fn ring_capacity(&mut self, ring_capacity: u32) -> &mut Self {
self.0.ring_capacity = Some(ring_capacity);
self
}
#[cfg(windows)]
pub fn metric(&mut self, metric: u16) -> &mut Self {
self.0.metric = Some(metric);
self
}
#[cfg(windows)]
pub fn delete_driver(&mut self, delete_driver: bool) -> &mut Self {
self.0.delete_driver = Some(delete_driver);
self
}
#[cfg(target_os = "linux")]
pub fn tx_queue_len(&mut self, tx_queue_len: u32) -> &mut Self {
self.0.tx_queue_len = Some(tx_queue_len);
self
}
#[cfg(target_os = "linux")]
pub fn offload(&mut self, offload: bool) -> &mut Self {
self.0.offload = Some(offload);
self
}
#[cfg(target_os = "linux")]
pub fn multi_queue(&mut self, multi_queue: bool) -> &mut Self {
self.0.multi_queue = Some(multi_queue);
self
}
#[cfg(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub fn packet_information(&mut self, packet_information: bool) -> &mut Self {
self.0.packet_information = Some(packet_information);
self
}
#[cfg(target_os = "macos")]
pub fn peer_feth<S: Into<String>>(&mut self, peer_feth: S) -> &mut Self {
self.0.peer_feth = Some(peer_feth.into());
self
}
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub fn associate_route(&mut self, associate_route: bool) -> &mut Self {
self.0.associate_route = Some(associate_route);
self
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
pub fn reuse_dev(&mut self, reuse: bool) -> &mut Self {
self.0.reuse_dev = Some(reuse);
self
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn persist(&mut self, persist: bool) -> &mut Self {
self.0.persist = Some(persist);
self
}
}
#[derive(Default)]
pub struct DeviceBuilder {
dev_name: Option<String>,
#[cfg(windows)]
description: Option<String>,
#[cfg(target_os = "macos")]
peer_feth: Option<String>,
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
associate_route: Option<bool>,
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
reuse_dev: Option<bool>,
#[cfg(any(target_os = "macos", target_os = "windows"))]
persist: Option<bool>,
enabled: Option<bool>,
mtu: Option<u16>,
#[cfg(windows)]
mtu_v6: Option<u16>,
ipv4: Option<IPV4>,
ipv6: Option<Vec<(io::Result<Ipv6Addr>, io::Result<u8>)>>,
layer: Option<Layer>,
#[cfg(any(
target_os = "windows",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "netbsd"
))]
mac_addr: Option<[u8; 6]>,
#[cfg(windows)]
device_guid: Option<u128>,
#[cfg(windows)]
wintun_log: Option<bool>,
#[cfg(windows)]
wintun_file: Option<String>,
#[cfg(windows)]
ring_capacity: Option<u32>,
#[cfg(windows)]
metric: Option<u16>,
#[cfg(windows)]
delete_driver: Option<bool>,
#[cfg(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
packet_information: Option<bool>,
#[cfg(target_os = "linux")]
tx_queue_len: Option<u32>,
#[cfg(target_os = "linux")]
offload: Option<bool>,
#[cfg(target_os = "linux")]
multi_queue: Option<bool>,
}
impl DeviceBuilder {
pub fn new() -> Self {
Self::default().enable(true)
}
pub fn name<S: Into<String>>(mut self, dev_name: S) -> Self {
self.dev_name = Some(dev_name.into());
self
}
#[cfg(windows)]
pub fn description<S: Into<String>>(mut self, description: S) -> Self {
self.description = Some(description.into());
self
}
pub fn mtu(mut self, mtu: u16) -> Self {
self.mtu = Some(mtu);
#[cfg(windows)]
{
self.mtu_v6 = Some(mtu);
}
self
}
#[cfg(windows)]
pub fn mtu_v4(mut self, mtu: u16) -> Self {
self.mtu = Some(mtu);
self
}
#[cfg(windows)]
pub fn mtu_v6(mut self, mtu: u16) -> Self {
self.mtu_v6 = Some(mtu);
self
}
#[cfg(any(
target_os = "windows",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos",
target_os = "netbsd"
))]
pub fn mac_addr(mut self, mac_addr: [u8; 6]) -> Self {
self.mac_addr = Some(mac_addr);
self
}
pub fn ipv4<IPv4: ToIpv4Address, Netmask: ToIpv4Netmask>(
mut self,
address: IPv4,
mask: Netmask,
destination: Option<IPv4>,
) -> Self {
self.ipv4 = Some((address.ipv4(), mask.prefix(), destination.map(|v| v.ipv4())));
self
}
pub fn ipv6<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
mut self,
address: IPv6,
mask: Netmask,
) -> Self {
if let Some(v) = &mut self.ipv6 {
v.push((address.ipv6(), mask.prefix()));
} else {
self.ipv6 = Some(vec![(address.ipv6(), mask.prefix())]);
}
self
}
pub fn ipv6_tuple<IPv6: ToIpv6Address, Netmask: ToIpv6Netmask>(
mut self,
addrs: &[(IPv6, Netmask)],
) -> Self {
if let Some(v) = &mut self.ipv6 {
for (address, mask) in addrs {
v.push((address.ipv6(), mask.prefix()));
}
} else {
self.ipv6 = Some(
addrs
.iter()
.map(|(ip, mask)| (ip.ipv6(), mask.prefix()))
.collect(),
);
}
self
}
pub fn layer(mut self, layer: Layer) -> Self {
self.layer = Some(layer);
self
}
#[cfg(windows)]
pub fn device_guid(mut self, device_guid: u128) -> Self {
self.device_guid = Some(device_guid);
self
}
#[cfg(windows)]
pub fn wintun_log(mut self, wintun_log: bool) -> Self {
self.wintun_log = Some(wintun_log);
self
}
#[cfg(windows)]
pub fn wintun_file(mut self, wintun_file: String) -> Self {
self.wintun_file = Some(wintun_file);
self
}
#[cfg(windows)]
pub fn ring_capacity(mut self, ring_capacity: u32) -> Self {
self.ring_capacity = Some(ring_capacity);
self
}
#[cfg(windows)]
pub fn metric(mut self, metric: u16) -> Self {
self.metric = Some(metric);
self
}
#[cfg(windows)]
pub fn delete_driver(mut self, delete_driver: bool) -> Self {
self.delete_driver = Some(delete_driver);
self
}
#[cfg(target_os = "linux")]
pub fn tx_queue_len(mut self, tx_queue_len: u32) -> Self {
self.tx_queue_len = Some(tx_queue_len);
self
}
#[cfg(target_os = "linux")]
pub fn offload(mut self, offload: bool) -> Self {
self.offload = Some(offload);
self
}
#[cfg(target_os = "linux")]
pub fn multi_queue(mut self, multi_queue: bool) -> Self {
self.multi_queue = Some(multi_queue);
self
}
#[cfg(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub fn packet_information(mut self, packet_information: bool) -> Self {
self.packet_information = Some(packet_information);
self
}
#[cfg(target_os = "macos")]
pub fn peer_feth<S: Into<String>>(mut self, peer_feth: S) -> Self {
self.peer_feth = Some(peer_feth.into());
self
}
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub fn associate_route(mut self, associate_route: bool) -> Self {
self.associate_route = Some(associate_route);
self
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
pub fn reuse_dev(mut self, reuse: bool) -> Self {
self.reuse_dev = Some(reuse);
self
}
#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn persist(mut self, persist: bool) -> Self {
self.persist = Some(persist);
self
}
pub fn enable(mut self, enable: bool) -> Self {
self.enabled = Some(enable);
self
}
pub fn inherit_enable_state(mut self) -> Self {
self.enabled = None;
self
}
pub(crate) fn build_config(&mut self) -> DeviceConfig {
DeviceConfig {
dev_name: self.dev_name.take(),
#[cfg(windows)]
description: self.description.take(),
#[cfg(target_os = "macos")]
peer_feth: self.peer_feth.take(),
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
associate_route: self.associate_route,
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "netbsd"))]
reuse_dev: self.reuse_dev,
#[cfg(any(target_os = "macos", target_os = "windows"))]
persist: self.persist,
layer: self.layer.take(),
#[cfg(windows)]
device_guid: self.device_guid.take(),
#[cfg(windows)]
wintun_log: self.wintun_log.take(),
#[cfg(windows)]
wintun_file: self.wintun_file.take(),
#[cfg(windows)]
ring_capacity: self.ring_capacity.take(),
#[cfg(windows)]
delete_driver: self.delete_driver.take(),
#[cfg(windows)]
mac_address: self.mac_addr.map(|v| {
use std::fmt::Write;
v.iter()
.fold(String::with_capacity(v.len() * 2), |mut s, b| {
write!(&mut s, "{b:02X}").unwrap();
s
})
}),
#[cfg(any(
target_os = "macos",
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
packet_information: self.packet_information.take(),
#[cfg(target_os = "linux")]
offload: self.offload.take(),
#[cfg(target_os = "linux")]
multi_queue: self.multi_queue.take(),
}
}
pub(crate) fn config(self, device: &DeviceImpl) -> io::Result<()> {
if let Some(mtu) = self.mtu {
device.set_mtu(mtu)?;
}
#[cfg(windows)]
if let Some(mtu) = self.mtu_v6 {
device.set_mtu_v6(mtu)?;
}
#[cfg(windows)]
if let Some(metric) = self.metric {
device.set_metric(metric)?;
}
#[cfg(target_os = "linux")]
if let Some(tx_queue_len) = self.tx_queue_len {
device.set_tx_queue_len(tx_queue_len)?;
}
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "macos",
target_os = "openbsd",
target_os = "netbsd"
))]
if let Some(mac_addr) = self.mac_addr {
device.set_mac_address(mac_addr)?;
}
if let Some((address, prefix, destination)) = self.ipv4 {
let prefix = prefix?;
let address = address?;
let destination = destination.transpose()?;
device.set_network_address(address, prefix, destination)?;
}
if let Some(ipv6) = self.ipv6 {
for (address, prefix) in ipv6 {
let prefix = prefix?;
let address = address?;
device.add_address_v6(address, prefix)?;
}
}
if let Some(enabled) = self.enabled {
device.enabled(enabled)?;
}
Ok(())
}
pub fn build_sync(mut self) -> io::Result<SyncDevice> {
let device = DeviceImpl::new(self.build_config())?;
self.config(&device)?;
Ok(SyncDevice(device))
}
#[cfg(any(feature = "async_io", feature = "async_tokio"))]
pub fn build_async(self) -> io::Result<crate::AsyncDevice> {
let sync_device = self.build_sync()?;
let device = crate::AsyncDevice::new_dev(sync_device.0)?;
Ok(device)
}
pub fn with<F: FnMut(&mut DeviceBuilderGuard)>(mut self, mut f: F) -> Self {
let mut borrow = DeviceBuilderGuard(&mut self);
f(&mut borrow);
self
}
}
pub trait ToIpv4Address {
fn ipv4(&self) -> io::Result<Ipv4Addr>;
}
impl ToIpv4Address for Ipv4Addr {
fn ipv4(&self) -> io::Result<Ipv4Addr> {
Ok(*self)
}
}
impl ToIpv4Address for IpAddr {
fn ipv4(&self) -> io::Result<Ipv4Addr> {
match self {
IpAddr::V4(ip) => Ok(*ip),
IpAddr::V6(_) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid address",
)),
}
}
}
impl ToIpv4Address for String {
fn ipv4(&self) -> io::Result<Ipv4Addr> {
self.as_str().ipv4()
}
}
impl ToIpv4Address for &str {
fn ipv4(&self) -> io::Result<Ipv4Addr> {
match Ipv4Addr::from_str(self) {
Ok(ip) => Ok(ip),
Err(_e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid IPv4 str",
)),
}
}
}
pub trait ToIpv6Address {
fn ipv6(&self) -> io::Result<Ipv6Addr>;
}
impl ToIpv6Address for Ipv6Addr {
fn ipv6(&self) -> io::Result<Ipv6Addr> {
Ok(*self)
}
}
impl ToIpv6Address for IpAddr {
fn ipv6(&self) -> io::Result<Ipv6Addr> {
match self {
IpAddr::V4(_) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid address",
)),
IpAddr::V6(ip) => Ok(*ip),
}
}
}
impl ToIpv6Address for String {
fn ipv6(&self) -> io::Result<Ipv6Addr> {
self.as_str().ipv6()
}
}
impl ToIpv6Address for &str {
fn ipv6(&self) -> io::Result<Ipv6Addr> {
match Ipv6Addr::from_str(self) {
Ok(ip) => Ok(ip),
Err(_e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid IPv6 str",
)),
}
}
}
pub trait ToIpv4Netmask {
fn prefix(&self) -> io::Result<u8>;
fn netmask(&self) -> io::Result<Ipv4Addr> {
let ip = u32::MAX
.checked_shl(32 - self.prefix()? as u32)
.unwrap_or(0);
Ok(Ipv4Addr::from(ip))
}
}
impl ToIpv4Netmask for u8 {
fn prefix(&self) -> io::Result<u8> {
if *self > 32 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid IP prefix length",
));
}
Ok(*self)
}
}
impl ToIpv4Netmask for Ipv4Addr {
fn prefix(&self) -> io::Result<u8> {
let ip = u32::from_be_bytes(self.octets());
if ip.leading_ones() != ip.count_ones() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid netmask",
));
}
Ok(ip.leading_ones() as u8)
}
}
impl ToIpv4Netmask for String {
fn prefix(&self) -> io::Result<u8> {
ToIpv4Netmask::prefix(&self.as_str())
}
}
impl ToIpv4Netmask for &str {
fn prefix(&self) -> io::Result<u8> {
match Ipv4Addr::from_str(self) {
Ok(ip) => ip.prefix(),
Err(_e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid netmask str",
)),
}
}
}
pub trait ToIpv6Netmask {
fn prefix(&self) -> io::Result<u8>;
fn netmask(&self) -> io::Result<Ipv6Addr> {
let ip = u128::MAX
.checked_shl(128 - self.prefix()? as u32)
.unwrap_or(0);
Ok(Ipv6Addr::from(ip))
}
}
impl ToIpv6Netmask for u8 {
fn prefix(&self) -> io::Result<u8> {
if *self > 128 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid IP prefix length",
));
}
Ok(*self)
}
}
impl ToIpv6Netmask for Ipv6Addr {
fn prefix(&self) -> io::Result<u8> {
let ip = u128::from_be_bytes(self.octets());
if ip.leading_ones() != ip.count_ones() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid netmask",
));
}
Ok(ip.leading_ones() as u8)
}
}
impl ToIpv6Netmask for String {
fn prefix(&self) -> io::Result<u8> {
ToIpv6Netmask::prefix(&self.as_str())
}
}
impl ToIpv6Netmask for &str {
fn prefix(&self) -> io::Result<u8> {
match Ipv6Addr::from_str(self) {
Ok(ip) => ip.prefix(),
Err(_e) => Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid netmask str",
)),
}
}
}