mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-07 10:14:35 +00:00
use winapi to config ip and route (remove dep on netsh) (#1079)
On some windows machines can not execut netsh. Also this avoid black cmd window when using gui.
This commit is contained in:
Generated
+4
-2
@@ -2058,8 +2058,10 @@ dependencies = [
|
|||||||
"version-compare",
|
"version-compare",
|
||||||
"which 7.0.3",
|
"which 7.0.3",
|
||||||
"wildmatch",
|
"wildmatch",
|
||||||
|
"winapi",
|
||||||
"windows 0.52.0",
|
"windows 0.52.0",
|
||||||
"windows-service",
|
"windows-service",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
"winreg 0.52.0",
|
"winreg 0.52.0",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
"zip",
|
"zip",
|
||||||
@@ -4144,7 +4146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"windows-targets 0.48.5",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -9798,7 +9800,7 @@ version = "0.1.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+20
-2
@@ -3,13 +3,29 @@
|
|||||||
{
|
{
|
||||||
"path": "."
|
"path": "."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "core",
|
||||||
|
"path": "easytier"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "gui",
|
"name": "gui",
|
||||||
"path": "easytier-gui"
|
"path": "easytier-gui"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "core",
|
"name": "web",
|
||||||
"path": "easytier"
|
"path": "easytier-web"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ffi",
|
||||||
|
"path": "easytier-contrib/easytier-ffi"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "magisk",
|
||||||
|
"path": "easytier-contrib/easytier-magisk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "openharmony",
|
||||||
|
"path": "easytier-contrib/easytier-ohrs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vpnservice",
|
"name": "vpnservice",
|
||||||
@@ -26,5 +42,7 @@
|
|||||||
"i18n-ally.sortKeys": true,
|
"i18n-ally.sortKeys": true,
|
||||||
// Disable the default formatter
|
// Disable the default formatter
|
||||||
"prettier.enable": false,
|
"prettier.enable": false,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnSaveMode": "modifications",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,6 +246,13 @@ windows = { version = "0.52.0", features = [
|
|||||||
encoding = "0.2"
|
encoding = "0.2"
|
||||||
winreg = "0.52"
|
winreg = "0.52"
|
||||||
windows-service = "0.7.0"
|
windows-service = "0.7.0"
|
||||||
|
windows-sys = { version = "0.52", features = [
|
||||||
|
"Win32_NetworkManagement_IpHelper",
|
||||||
|
"Win32_NetworkManagement_Ndis",
|
||||||
|
"Win32_Networking_WinSock",
|
||||||
|
"Win32_Foundation"
|
||||||
|
]}
|
||||||
|
winapi = { version = "0.3.9", features = ["impl-default"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tonic-build = "0.12"
|
tonic-build = "0.12"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
|
||||||
|
|
||||||
use super::{cidr_to_subnet_mask, run_shell_cmd, Error, IfConfiguerTrait};
|
use super::{cidr_to_subnet_mask, run_shell_cmd, Error, IfConfiguerTrait};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
|
|
||||||
pub struct MacIfConfiger {}
|
pub struct MacIfConfiger {}
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -66,12 +66,17 @@ impl IfConfiguerTrait for MacIfConfiger {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Addr>) -> Result<(), Error> {
|
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Inet>) -> Result<(), Error> {
|
||||||
if ip.is_none() {
|
if ip.is_none() {
|
||||||
run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await
|
run_shell_cmd(format!("ifconfig {} inet delete", name).as_str()).await
|
||||||
} else {
|
} else {
|
||||||
run_shell_cmd(
|
run_shell_cmd(
|
||||||
format!("ifconfig {} inet {} delete", name, ip.unwrap().to_string()).as_str(),
|
format!(
|
||||||
|
"ifconfig {} inet {} delete",
|
||||||
|
name,
|
||||||
|
ip.unwrap().address().to_string()
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@@ -87,15 +92,13 @@ impl IfConfiguerTrait for MacIfConfiger {
|
|||||||
address: std::net::Ipv6Addr,
|
address: std::net::Ipv6Addr,
|
||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
run_shell_cmd(
|
run_shell_cmd(format!("ifconfig {} inet6 {}/{} add", name, address, cidr_prefix).as_str())
|
||||||
format!("ifconfig {} inet6 {}/{} add", name, address, cidr_prefix).as_str(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv6(&self, name: &str, ip: Option<std::net::Ipv6Addr>) -> Result<(), Error> {
|
async fn remove_ipv6(&self, name: &str, ip: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||||
if let Some(ip) = ip {
|
if let Some(ip) = ip {
|
||||||
run_shell_cmd(format!("ifconfig {} inet6 {} delete", name, ip).as_str()).await
|
run_shell_cmd(format!("ifconfig {} inet6 {} delete", name, ip.address()).as_str()).await
|
||||||
} else {
|
} else {
|
||||||
// Remove all IPv6 addresses is more complex on macOS, just succeed
|
// Remove all IPv6 addresses is more complex on macOS, just succeed
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ mod darwin;
|
|||||||
#[cfg(any(target_os = "linux"))]
|
#[cfg(any(target_os = "linux"))]
|
||||||
mod netlink;
|
mod netlink;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
mod win;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
mod windows;
|
mod windows;
|
||||||
|
|
||||||
mod route;
|
mod route;
|
||||||
@@ -10,6 +12,7 @@ mod route;
|
|||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
|
|
||||||
use super::error::Error;
|
use super::error::Error;
|
||||||
@@ -69,10 +72,10 @@ pub trait IfConfiguerTrait: Send + Sync {
|
|||||||
async fn set_link_status(&self, _name: &str, _up: bool) -> Result<(), Error> {
|
async fn set_link_status(&self, _name: &str, _up: bool) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn remove_ip(&self, _name: &str, _ip: Option<Ipv4Addr>) -> Result<(), Error> {
|
async fn remove_ip(&self, _name: &str, _ip: Option<Ipv4Inet>) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn remove_ipv6(&self, _name: &str, _ip: Option<Ipv6Addr>) -> Result<(), Error> {
|
async fn remove_ipv6(&self, _name: &str, _ip: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn wait_interface_show(&self, _name: &str) -> Result<(), Error> {
|
async fn wait_interface_show(&self, _name: &str) -> Result<(), Error> {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use std::{
|
|||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use cidr::IpInet;
|
use cidr::{IpInet, Ipv4Inet, Ipv6Inet};
|
||||||
use netlink_packet_core::{
|
use netlink_packet_core::{
|
||||||
NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload, NetlinkSerializable,
|
NetlinkDeserializable, NetlinkHeader, NetlinkMessage, NetlinkPayload, NetlinkSerializable,
|
||||||
NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NLM_F_EXCL, NLM_F_REQUEST,
|
NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NLM_F_EXCL, NLM_F_REQUEST,
|
||||||
@@ -473,7 +473,7 @@ impl IfConfiguerTrait for NetlinkIfConfiger {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Addr>) -> Result<(), Error> {
|
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Inet>) -> Result<(), Error> {
|
||||||
if ip.is_none() {
|
if ip.is_none() {
|
||||||
let addrs = Self::list_addresses(name)?;
|
let addrs = Self::list_addresses(name)?;
|
||||||
for addr in addrs {
|
for addr in addrs {
|
||||||
@@ -483,8 +483,8 @@ impl IfConfiguerTrait for NetlinkIfConfiger {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ip = ip.unwrap();
|
let ip = ip.unwrap();
|
||||||
let prefix_len = Self::get_prefix_len(name, ip)?;
|
let prefix_len = Self::get_prefix_len(name, ip.address())?;
|
||||||
Self::remove_one_ip(name, ip, prefix_len)?;
|
Self::remove_one_ip(name, ip.address(), prefix_len)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -519,7 +519,7 @@ impl IfConfiguerTrait for NetlinkIfConfiger {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv6(&self, name: &str, ip: Option<std::net::Ipv6Addr>) -> Result<(), Error> {
|
async fn remove_ipv6(&self, name: &str, ip: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||||
if ip.is_none() {
|
if ip.is_none() {
|
||||||
let addrs = Self::list_addresses(name)?;
|
let addrs = Self::list_addresses(name)?;
|
||||||
for addr in addrs {
|
for addr in addrs {
|
||||||
@@ -530,8 +530,8 @@ impl IfConfiguerTrait for NetlinkIfConfiger {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let ipv6 = ip.unwrap();
|
let ipv6 = ip.unwrap();
|
||||||
let prefix_len = Self::get_prefix_len_ipv6(name, ipv6)?;
|
let prefix_len = Self::get_prefix_len_ipv6(name, ipv6.address())?;
|
||||||
Self::remove_one_ipv6(name, ipv6, prefix_len)?;
|
Self::remove_one_ipv6(name, ipv6.address(), prefix_len)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -0,0 +1,745 @@
|
|||||||
|
//
|
||||||
|
// Port supporting code from wireguard-windows, as used by 3rd-party/wireguard-go, to Rust
|
||||||
|
// This file implements functionality similar to: wireguard-windows/tunnel/winipcfg/luid.go
|
||||||
|
//
|
||||||
|
// ATTENTION: NOT included are DNS() and SetDNS() - functions to query and set DNS servers for a network interface.
|
||||||
|
//
|
||||||
|
|
||||||
|
use super::netsh;
|
||||||
|
use super::types::*;
|
||||||
|
use cidr::Ipv4Inet;
|
||||||
|
use cidr::Ipv6Inet;
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
use std::ptr;
|
||||||
|
use winapi::shared::{
|
||||||
|
guiddef::GUID, ifdef::NET_LUID, netioapi::*, nldef::*, winerror::*, ws2def::*, ws2ipdef::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct InterfaceLuid {
|
||||||
|
luid: NET_LUID,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterfaceLuid {
|
||||||
|
pub fn new(luid_value: u64) -> Self {
|
||||||
|
InterfaceLuid {
|
||||||
|
luid: winapi::shared::ifdef::NET_LUID_LH { Value: luid_value },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn luid(&self) -> NET_LUID {
|
||||||
|
self.luid
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get_ip_interface method retrieves IP information for the specified interface on the local computer.
|
||||||
|
pub fn get_ip_interface(
|
||||||
|
&self,
|
||||||
|
family: ADDRESS_FAMILY,
|
||||||
|
) -> Result<MIB_IPINTERFACE_ROW, NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPINTERFACE_ROW::default();
|
||||||
|
unsafe { InitializeIpInterfaceEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.Family = family;
|
||||||
|
|
||||||
|
let result = unsafe { GetIpInterfaceEntry(&mut row) };
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(row)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-setipinterfaceentry
|
||||||
|
/// If only InterfaceIndex was specified, SetIpInterfaceEntry() will modify ipif with a correct InterfaceLuid
|
||||||
|
pub fn set_ip_interface(&self, ipif: *mut MIB_IPINTERFACE_ROW) -> Result<(), NETIO_STATUS> {
|
||||||
|
let result = unsafe { SetIpInterfaceEntry(ipif) };
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get_interface method retrieves information for the specified adapter on the local computer.
|
||||||
|
/// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getifentry2
|
||||||
|
pub fn get_interface(&self) -> Result<MIB_IF_ROW2, NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IF_ROW2 {
|
||||||
|
InterfaceLuid: self.luid,
|
||||||
|
..MIB_IF_ROW2::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = unsafe { GetIfEntry2(&mut row) };
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(row)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GUID method converts a locally unique identifier (LUID) for a network interface to a globally unique identifier (GUID) for the interface.
|
||||||
|
/// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceluidtoguid
|
||||||
|
pub fn get_guid(&self) -> Result<GUID, NETIO_STATUS> {
|
||||||
|
let mut interface_guid = GUID::default();
|
||||||
|
|
||||||
|
let result = unsafe { ConvertInterfaceLuidToGuid(&self.luid, &mut interface_guid) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(interface_guid)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// luid_from_guid function converts a globally unique identifier (GUID) for a network interface to the locally unique identifier (LUID) for the interface.
|
||||||
|
/// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceguidtoluid
|
||||||
|
pub fn luid_from_guid(interface_guid: &GUID) -> Result<Self, NETIO_STATUS> {
|
||||||
|
let mut interface_luid = NET_LUID::default();
|
||||||
|
|
||||||
|
let result = unsafe { ConvertInterfaceGuidToLuid(interface_guid, &mut interface_luid) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(Self {
|
||||||
|
luid: interface_luid,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// luid_from_index function converts a local index for a network interface to the locally unique identifier (LUID) for the interface.
|
||||||
|
/// https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-convertinterfaceindextoluid
|
||||||
|
pub fn luid_from_index(interface_index: u32) -> Result<Self, NETIO_STATUS> {
|
||||||
|
let mut interface_luid = NET_LUID::default();
|
||||||
|
|
||||||
|
let result = unsafe { ConvertInterfaceIndexToLuid(interface_index, &mut interface_luid) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(Self {
|
||||||
|
luid: interface_luid,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get_from_ipv4_address method returns MibUnicastIPAddressRow struct that matches to provided 'ip' argument. Corresponds to GetUnicastIpAddressEntry
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddressentry)
|
||||||
|
pub fn get_from_ipv4_address(
|
||||||
|
&self,
|
||||||
|
ip: &Ipv4Addr,
|
||||||
|
) -> Result<MIB_UNICASTIPADDRESS_ROW, NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
unsafe { *row.Address.Ipv4_mut() = convert_ipv4addr_to_sockaddr(ip) };
|
||||||
|
|
||||||
|
let result = unsafe { GetUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(row)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get_from_ipv6_address method returns MibUnicastIPAddressRow struct that matches to provided 'ip' argument. Corresponds to GetUnicastIpAddressEntry
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getunicastipaddressentry)
|
||||||
|
pub fn get_from_ipv6_address(
|
||||||
|
&self,
|
||||||
|
ip: &Ipv6Addr,
|
||||||
|
) -> Result<MIB_UNICASTIPADDRESS_ROW, NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
unsafe { *row.Address.Ipv6_mut() = convert_ipv6addr_to_sockaddr(ip) };
|
||||||
|
|
||||||
|
let result = unsafe { GetUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(row)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_ipv4_address method adds new unicast IP address to the interface. Corresponds to CreateUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry).
|
||||||
|
pub fn add_ipv4_address(&self, address: &Ipv4Inet) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe { *row.Address.Ipv4_mut() = convert_ipv4addr_to_sockaddr(&address.address()) };
|
||||||
|
row.OnLinkPrefixLength = address.network_length();
|
||||||
|
|
||||||
|
let result = unsafe { CreateUnicastIpAddressEntry(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_ipv6_address method adds new unicast IP address to the interface. Corresponds to CreateUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry).
|
||||||
|
pub fn add_ipv6_address(&self, address: &Ipv6Inet) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe { *row.Address.Ipv6_mut() = convert_ipv6addr_to_sockaddr(&address.address()) };
|
||||||
|
row.OnLinkPrefixLength = address.network_length();
|
||||||
|
|
||||||
|
let result = unsafe { CreateUnicastIpAddressEntry(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_ipv4_addresses method adds multiple new unicast IP addresses to the interface. Corresponds to CreateUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry).
|
||||||
|
pub fn add_ipv4_addresses(
|
||||||
|
&self,
|
||||||
|
addresses: impl IntoIterator<Item = Ipv4Inet>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
for ip in addresses.into_iter().enumerate() {
|
||||||
|
self.add_ipv4_address(&ip.1)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_ipv6_addresses method adds multiple new unicast IP addresses to the interface. Corresponds to CreateUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createunicastipaddressentry).
|
||||||
|
pub fn add_ipv6_addresses(
|
||||||
|
&self,
|
||||||
|
addresses: impl IntoIterator<Item = Ipv6Inet>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
for ip in addresses.into_iter().enumerate() {
|
||||||
|
self.add_ipv6_address(&ip.1)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set_ipv4_addresses method sets new unicast IP addresses to the interface.
|
||||||
|
pub fn set_ipv4_addresses(
|
||||||
|
&self,
|
||||||
|
addresses: impl IntoIterator<Item = Ipv4Inet>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_ipv4_addresses()?;
|
||||||
|
self.add_ipv4_addresses(addresses)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set_ipv6_addresses method sets new unicast IP addresses to the interface.
|
||||||
|
pub fn set_ipv6_addresses(
|
||||||
|
&self,
|
||||||
|
addresses: impl IntoIterator<Item = Ipv6Inet>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_ipv6_addresses()?;
|
||||||
|
self.add_ipv6_addresses(addresses)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// delete_ipv4_address method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry).
|
||||||
|
pub fn delete_ipv4_address(&self, address: &Ipv4Inet) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe { *row.Address.Ipv4_mut() = convert_ipv4addr_to_sockaddr(&address.address()) };
|
||||||
|
row.OnLinkPrefixLength = address.network_length();
|
||||||
|
|
||||||
|
let result = unsafe { DeleteUnicastIpAddressEntry(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// delete_ipv4_address method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry).
|
||||||
|
pub fn delete_ipv4_address2(
|
||||||
|
&self,
|
||||||
|
address: *const SOCKADDR_IN,
|
||||||
|
prefix_len: u8,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
assert!(!address.is_null());
|
||||||
|
unsafe { *row.Address.Ipv4_mut() = *address };
|
||||||
|
row.OnLinkPrefixLength = prefix_len;
|
||||||
|
|
||||||
|
let result = unsafe { DeleteUnicastIpAddressEntry(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// delete_ipv6_address method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry).
|
||||||
|
pub fn delete_ipv6_address(&self, address: &Ipv6Inet) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe { *row.Address.Ipv6_mut() = convert_ipv6addr_to_sockaddr(&address.address()) };
|
||||||
|
row.OnLinkPrefixLength = address.network_length();
|
||||||
|
|
||||||
|
let result = unsafe { DeleteUnicastIpAddressEntry(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// delete_ipv6_address method deletes interface's unicast IP address. Corresponds to DeleteUnicastIpAddressEntry function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteunicastipaddressentry).
|
||||||
|
pub fn delete_ipv6_address2(
|
||||||
|
&self,
|
||||||
|
address: *const SOCKADDR_IN6,
|
||||||
|
prefix_len: u8,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_UNICASTIPADDRESS_ROW::default();
|
||||||
|
unsafe { InitializeUnicastIpAddressEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.DadState = IpDadStatePreferred;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
assert!(!address.is_null());
|
||||||
|
unsafe { *row.Address.Ipv6_mut() = *address };
|
||||||
|
row.OnLinkPrefixLength = prefix_len;
|
||||||
|
|
||||||
|
let result = unsafe { DeleteUnicastIpAddressEntry(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_ip_addresses method deletes all interface's unicast IP addresses.
|
||||||
|
pub fn flush_ip_addresses(&self, address_family: ADDRESS_FAMILY) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut p_table: PMIB_UNICASTIPADDRESS_TABLE = ptr::null_mut();
|
||||||
|
let result = unsafe { GetUnicastIpAddressTable(address_family, &mut p_table) };
|
||||||
|
if NO_ERROR != result {
|
||||||
|
return Err(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!p_table.is_null());
|
||||||
|
let num_entries = unsafe { *p_table }.NumEntries;
|
||||||
|
let x_table = unsafe { *p_table }.Table.as_ptr();
|
||||||
|
for i in 0..num_entries {
|
||||||
|
let current_entry = unsafe { x_table.add(i as _) };
|
||||||
|
if unsafe { (*current_entry).InterfaceLuid.Value } == self.luid.Value {
|
||||||
|
unsafe { DeleteUnicastIpAddressEntry(current_entry) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { FreeMibTable(p_table as _) };
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_ipv4_addresses method deletes all interface's unicast IP addresses.
|
||||||
|
pub fn flush_ipv4_addresses(&self) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_ip_addresses(AF_INET as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_ipv6_addresses method deletes all interface's unicast IP addresses.
|
||||||
|
pub fn flush_ipv6_addresses(&self) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_ip_addresses(AF_INET6 as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// route_ipv4 method returns route determined with the input arguments. Corresponds to GetIpForwardEntry2 function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardentry2).
|
||||||
|
/// NOTE: If the corresponding route isn't found, the method will return error.
|
||||||
|
pub fn route_ipv4(
|
||||||
|
&self,
|
||||||
|
destination: &Ipv4Inet,
|
||||||
|
next_hop: &Ipv4Addr,
|
||||||
|
) -> Result<MIB_IPFORWARD_ROW2, NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPFORWARD_ROW2::default();
|
||||||
|
unsafe { InitializeIpForwardEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*row.DestinationPrefix.Prefix.Ipv4_mut() =
|
||||||
|
convert_ipv4addr_to_sockaddr(&destination.address())
|
||||||
|
};
|
||||||
|
row.DestinationPrefix.PrefixLength = destination.network_length();
|
||||||
|
|
||||||
|
unsafe { *row.NextHop.Ipv4_mut() = convert_ipv4addr_to_sockaddr(next_hop) };
|
||||||
|
|
||||||
|
let result = unsafe { GetIpForwardEntry2(&mut row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(row)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// route_ipv6 method returns route determined with the input arguments. Corresponds to GetIpForwardEntry2 function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-getipforwardentry2).
|
||||||
|
/// NOTE: If the corresponding route isn't found, the method will return error.
|
||||||
|
pub fn route_ipv6(
|
||||||
|
&self,
|
||||||
|
destination: &Ipv6Inet,
|
||||||
|
next_hop: &Ipv6Addr,
|
||||||
|
) -> Result<MIB_IPFORWARD_ROW2, NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPFORWARD_ROW2::default();
|
||||||
|
unsafe { InitializeIpForwardEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*row.DestinationPrefix.Prefix.Ipv6_mut() =
|
||||||
|
convert_ipv6addr_to_sockaddr(&destination.address())
|
||||||
|
};
|
||||||
|
row.DestinationPrefix.PrefixLength = destination.network_length();
|
||||||
|
|
||||||
|
unsafe { *row.NextHop.Ipv6_mut() = convert_ipv6addr_to_sockaddr(next_hop) };
|
||||||
|
|
||||||
|
let result = unsafe { GetIpForwardEntry2(&mut row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(row)
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_route_ipv4 method adds a route to the interface. Corresponds to CreateIpForwardEntry2 function, with added splitDefault feature.
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createipforwardentry2)
|
||||||
|
pub fn add_route_ipv4(
|
||||||
|
&self,
|
||||||
|
destination: &Ipv4Inet,
|
||||||
|
next_hop: &Ipv4Addr,
|
||||||
|
metric: u32,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPFORWARD_ROW2::default();
|
||||||
|
unsafe { InitializeIpForwardEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*row.DestinationPrefix.Prefix.Ipv4_mut() =
|
||||||
|
convert_ipv4addr_to_sockaddr(&destination.address())
|
||||||
|
};
|
||||||
|
row.DestinationPrefix.PrefixLength = destination.network_length();
|
||||||
|
|
||||||
|
unsafe { *row.NextHop.Ipv4_mut() = convert_ipv4addr_to_sockaddr(next_hop) };
|
||||||
|
|
||||||
|
row.Metric = metric;
|
||||||
|
|
||||||
|
let result = unsafe { CreateIpForwardEntry2(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_route_ipv6 method adds a route to the interface. Corresponds to CreateIpForwardEntry2 function, with added splitDefault feature.
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-createipforwardentry2)
|
||||||
|
pub fn add_route_ipv6(
|
||||||
|
&self,
|
||||||
|
destination: &Ipv6Inet,
|
||||||
|
next_hop: &Ipv6Addr,
|
||||||
|
metric: u32,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPFORWARD_ROW2::default();
|
||||||
|
unsafe { InitializeIpForwardEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
row.ValidLifetime = 0xffffffff;
|
||||||
|
row.PreferredLifetime = 0xffffffff;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*row.DestinationPrefix.Prefix.Ipv6_mut() =
|
||||||
|
convert_ipv6addr_to_sockaddr(&destination.address())
|
||||||
|
};
|
||||||
|
row.DestinationPrefix.PrefixLength = destination.network_length();
|
||||||
|
|
||||||
|
unsafe { *row.NextHop.Ipv6_mut() = convert_ipv6addr_to_sockaddr(next_hop) };
|
||||||
|
|
||||||
|
row.Metric = metric;
|
||||||
|
|
||||||
|
let result = unsafe { CreateIpForwardEntry2(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_routes_ipv4 method adds multiple routes to the interface
|
||||||
|
pub fn add_routes_ipv4(
|
||||||
|
&self,
|
||||||
|
routes_data: impl IntoIterator<Item = RouteDataIpv4>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
for rd in routes_data.into_iter().enumerate() {
|
||||||
|
self.add_route_ipv4(&rd.1.destination, &rd.1.next_hop, rd.1.metric)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// add_routes_ipv6 method adds multiple routes to the interface
|
||||||
|
pub fn add_routes_ipv6(
|
||||||
|
&self,
|
||||||
|
routes_data: impl IntoIterator<Item = RouteDataIpv6>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
for rd in routes_data.into_iter().enumerate() {
|
||||||
|
self.add_route_ipv6(&rd.1.destination, &rd.1.next_hop, rd.1.metric)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set_routes_ipv4 method sets (flush than add) multiple routes to the interface.
|
||||||
|
pub fn set_routes_ipv4(
|
||||||
|
&self,
|
||||||
|
routes_data: impl IntoIterator<Item = RouteDataIpv4>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_routes_ipv4()?;
|
||||||
|
self.add_routes_ipv4(routes_data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set_routes_ipv6 method sets (flush than add) multiple routes to the interface.
|
||||||
|
pub fn set_routes_ipv6(
|
||||||
|
&self,
|
||||||
|
routes_data: impl IntoIterator<Item = RouteDataIpv6>,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_routes_ipv6()?;
|
||||||
|
self.add_routes_ipv6(routes_data)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// delete_route_ipv4 method deletes a route that matches the criteria. Corresponds to DeleteIpForwardEntry2 function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteipforwardentry2).
|
||||||
|
pub fn delete_route_ipv4(
|
||||||
|
&self,
|
||||||
|
destination: &Ipv4Inet,
|
||||||
|
next_hop: &Ipv4Addr,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPFORWARD_ROW2::default();
|
||||||
|
unsafe { InitializeIpForwardEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*row.DestinationPrefix.Prefix.Ipv4_mut() =
|
||||||
|
convert_ipv4addr_to_sockaddr(&destination.address())
|
||||||
|
};
|
||||||
|
row.DestinationPrefix.PrefixLength = destination.network_length();
|
||||||
|
|
||||||
|
unsafe { *row.NextHop.Ipv4_mut() = convert_ipv4addr_to_sockaddr(next_hop) };
|
||||||
|
|
||||||
|
let result = unsafe { GetIpForwardEntry2(&mut row) };
|
||||||
|
if NO_ERROR != result {
|
||||||
|
return Err(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = unsafe { DeleteIpForwardEntry2(&row) };
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// delete_route_ipv6 method deletes a route that matches the criteria. Corresponds to DeleteIpForwardEntry2 function
|
||||||
|
/// (https://docs.microsoft.com/en-us/windows/desktop/api/netioapi/nf-netioapi-deleteipforwardentry2).
|
||||||
|
pub fn delete_route_ipv6(
|
||||||
|
&self,
|
||||||
|
destination: &Ipv6Inet,
|
||||||
|
next_hop: &Ipv6Addr,
|
||||||
|
) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPFORWARD_ROW2::default();
|
||||||
|
unsafe { InitializeIpForwardEntry(&mut row) };
|
||||||
|
|
||||||
|
row.InterfaceLuid = self.luid;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*row.DestinationPrefix.Prefix.Ipv6_mut() =
|
||||||
|
convert_ipv6addr_to_sockaddr(&destination.address())
|
||||||
|
};
|
||||||
|
row.DestinationPrefix.PrefixLength = destination.network_length();
|
||||||
|
|
||||||
|
unsafe { *row.NextHop.Ipv6_mut() = convert_ipv6addr_to_sockaddr(next_hop) };
|
||||||
|
|
||||||
|
let result = unsafe { GetIpForwardEntry2(&mut row) };
|
||||||
|
if NO_ERROR != result {
|
||||||
|
return Err(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = unsafe { DeleteIpForwardEntry2(&row) };
|
||||||
|
|
||||||
|
if NO_ERROR == result {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_routes method deletes all interface's routes.
|
||||||
|
/// It continues on failures, and returns the last error afterwards.
|
||||||
|
pub fn flush_routes(&self, address_family: ADDRESS_FAMILY) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut last_error: NETIO_STATUS = NO_ERROR;
|
||||||
|
|
||||||
|
let mut p_table: PMIB_IPFORWARD_TABLE2 = ptr::null_mut();
|
||||||
|
let result = unsafe { GetIpForwardTable2(address_family, &mut p_table) };
|
||||||
|
if NO_ERROR != result {
|
||||||
|
return Err(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(!p_table.is_null());
|
||||||
|
let num_entries = unsafe { *p_table }.NumEntries;
|
||||||
|
let x_table = unsafe { *p_table }.Table.as_ptr();
|
||||||
|
for i in 0..num_entries {
|
||||||
|
let current_entry = unsafe { x_table.add(i as _) };
|
||||||
|
if unsafe { (*current_entry).InterfaceLuid.Value } == self.luid.Value {
|
||||||
|
let result = unsafe { DeleteIpForwardEntry2(current_entry) };
|
||||||
|
if NO_ERROR != result {
|
||||||
|
last_error = result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { FreeMibTable(p_table as _) };
|
||||||
|
|
||||||
|
if NO_ERROR == last_error {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_routes_ipv4 method deletes all interface's routes.
|
||||||
|
/// It continues on failures, and returns the last error afterwards.
|
||||||
|
pub fn flush_routes_ipv4(&self) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_routes(AF_INET as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_routes_ipv6 method deletes all interface's routes.
|
||||||
|
/// It continues on failures, and returns the last error afterwards.
|
||||||
|
pub fn flush_routes_ipv6(&self) -> Result<(), NETIO_STATUS> {
|
||||||
|
self.flush_routes(AF_INET6 as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_dns method clears all DNS servers associated with the adapter.
|
||||||
|
fn flush_dns(&self, family: ADDRESS_FAMILY) -> Result<(), String> {
|
||||||
|
let ip_itf = match self.get_ip_interface(family) {
|
||||||
|
Ok(ip_itf) => ip_itf,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(String::from("Failed to obtain interface"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
netsh::flush_dns(family, ip_itf.InterfaceIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_dns_ipv4 method clears all DNS servers associated with the adapter.
|
||||||
|
pub fn flush_dns_ipv4(&self) -> Result<(), String> {
|
||||||
|
self.flush_dns(AF_INET as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// flush_dns_ipv6 method clears all DNS servers associated with the adapter.
|
||||||
|
pub fn flush_dns_ipv6(&self) -> Result<(), String> {
|
||||||
|
self.flush_dns(AF_INET6 as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets MTU on the interface
|
||||||
|
/// TODO: Set IP and other things in here too, so the code is more organized
|
||||||
|
pub fn set_iface_config(&self, mtu: u32) -> Result<(), NETIO_STATUS> {
|
||||||
|
// SAFETY: Both NET_LUID_LH unions should be the same. We're just copying out
|
||||||
|
// the u64 value and re-wrapping it, since wintun doesn't refer to the windows
|
||||||
|
// crate's version of NET_LUID_LH.
|
||||||
|
self.try_set_mtu(AF_INET as ADDRESS_FAMILY, mtu)?;
|
||||||
|
self.try_set_mtu(AF_INET6 as ADDRESS_FAMILY, mtu)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_set_mtu(&self, family: ADDRESS_FAMILY, mut mtu: u32) -> Result<(), NETIO_STATUS> {
|
||||||
|
let mut row = MIB_IPINTERFACE_ROW {
|
||||||
|
Family: family,
|
||||||
|
InterfaceLuid: self.luid,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// SAFETY: TODO
|
||||||
|
let error = unsafe { GetIpInterfaceEntry(&mut row) };
|
||||||
|
if error != NO_ERROR {
|
||||||
|
if family == (AF_INET6 as ADDRESS_FAMILY) && error == ERROR_NOT_FOUND {
|
||||||
|
tracing::debug!(?family, "Couldn't set MTU, maybe IPv6 is disabled.");
|
||||||
|
} else {
|
||||||
|
tracing::warn!(?family, "Couldn't set MTU: {}", error);
|
||||||
|
}
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if family == (AF_INET6 as ADDRESS_FAMILY) {
|
||||||
|
// ipv6 mtu must be at least 1280
|
||||||
|
mtu = 1280.max(mtu);
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/54857292/setipinterfaceentry-returns-error-invalid-parameter
|
||||||
|
row.SitePrefixLength = 0;
|
||||||
|
|
||||||
|
row.NlMtu = mtu;
|
||||||
|
|
||||||
|
// SAFETY: TODO
|
||||||
|
let ret = unsafe { SetIpInterfaceEntry(&mut row) };
|
||||||
|
if NO_ERROR == ret {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod netsh;
|
||||||
|
pub mod types;
|
||||||
|
pub mod luid;
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
//
|
||||||
|
// Port supporting code for wireguard-nt from wireguard-windows v0.5.3 to Rust
|
||||||
|
// This file replicates the functionality of wireguard-windows/tunnel/winipcfg/netsh.go
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
use winapi::shared::ws2def::{ADDRESS_FAMILY, AF_INET, AF_INET6};
|
||||||
|
|
||||||
|
pub fn flush_dns(family: ADDRESS_FAMILY, if_index: u32) -> Result<(), String> {
|
||||||
|
let proto_name = match family as i32 {
|
||||||
|
AF_INET => "ipv4",
|
||||||
|
AF_INET6 => "ipv6",
|
||||||
|
_ => {
|
||||||
|
return Err(String::from("Invalid address family"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//let netsh_params = format!("interface {proto} set dnsservers name={itf} source=static address=none validate=no register=both", proto=proto_name, itf=ip_itf.InterfaceIndex);
|
||||||
|
let ret_netsh = Command::new("netsh.exe")
|
||||||
|
.arg("interface")
|
||||||
|
.arg(proto_name)
|
||||||
|
.arg("set")
|
||||||
|
.arg("dnsservers")
|
||||||
|
.arg(format!("name={}", if_index))
|
||||||
|
.arg("source=static")
|
||||||
|
.arg("address=none")
|
||||||
|
.arg("validate=no")
|
||||||
|
.arg("register=both")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.output();
|
||||||
|
match ret_netsh {
|
||||||
|
Ok(output) => {
|
||||||
|
// netsh.exe returns error messages only and is silent upon success. BUT then it will return \r\n, so we need to look at the lines.
|
||||||
|
if let Ok(stdout_str) = String::from_utf8(output.stdout) {
|
||||||
|
if stdout_str.is_empty() || stdout_str == "\r\n" {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// TODO: ignore "There are no Domain Name Servers (DNS) configured on this computer."
|
||||||
|
// Is this string localized?
|
||||||
|
Err(stdout_str)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(String::from("Could not parse netsh output"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => Err(String::from("Failed to execute command")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Please execute flush_dns() first, as written in the original source code.
|
||||||
|
fn add_dns(family: ADDRESS_FAMILY, if_index: u32, dnses: &[String]) -> Result<(), String> {
|
||||||
|
let proto_name = match family as i32 {
|
||||||
|
AF_INET => "ipv4",
|
||||||
|
AF_INET6 => "ipv6",
|
||||||
|
_ => {
|
||||||
|
return Err(String::from("Invalid address family"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// "interface ipv4 add dnsservers name=%d address=%s validate=no"
|
||||||
|
let ret_netsh = Command::new("netsh.exe")
|
||||||
|
.arg("interface")
|
||||||
|
.arg(proto_name)
|
||||||
|
.arg("add")
|
||||||
|
.arg("dnsservers")
|
||||||
|
.arg(format!("name={}", if_index))
|
||||||
|
.arg(format!(
|
||||||
|
"address={}",
|
||||||
|
dnses
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",")
|
||||||
|
))
|
||||||
|
.arg("validate=no")
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.output();
|
||||||
|
match ret_netsh {
|
||||||
|
Ok(output) => {
|
||||||
|
// netsh.exe returns error messages only and is silent upon success. BUT then it will return \r\n, so we need to look at the lines.
|
||||||
|
if let Ok(stdout_str) = String::from_utf8(output.stdout) {
|
||||||
|
if stdout_str.is_empty() || stdout_str == "\r\n" {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
// TODO: ignore "There are no Domain Name Servers (DNS) configured on this computer."
|
||||||
|
// Is this string localized?
|
||||||
|
Err(stdout_str)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(String::from("Could not parse netsh output"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(_) => Err(String::from("Failed to execute command")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dns_ipv4(if_index: u32, dnses: &[Ipv4Addr]) -> Result<(), String> {
|
||||||
|
flush_dns(AF_INET as _, if_index)?;
|
||||||
|
if dnses.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let dnses_str: Vec<String> = dnses.iter().map(|addr| addr.to_string()).collect();
|
||||||
|
add_dns(AF_INET as _, if_index, &dnses_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_dns_ipv6(if_index: u32, dnses: &[Ipv6Addr]) -> Result<(), String> {
|
||||||
|
flush_dns(AF_INET6 as _, if_index)?;
|
||||||
|
if dnses.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let dnses_str: Vec<String> = dnses.iter().map(|addr| addr.to_string()).collect();
|
||||||
|
add_dns(AF_INET6 as _, if_index, &dnses_str)
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// Port supporting code for wireguard-nt from wireguard-windows v0.5.3 to Rust
|
||||||
|
// This file replicates parts of wireguard-windows/tunnel/winipcfg/types.go
|
||||||
|
//
|
||||||
|
|
||||||
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
|
use std::os::windows::prelude::*;
|
||||||
|
use winapi::shared::ws2def::*;
|
||||||
|
use winapi::shared::ws2ipdef::*;
|
||||||
|
|
||||||
|
#[derive(Hash, Eq, PartialEq, Debug)]
|
||||||
|
pub struct RouteDataIpv4 {
|
||||||
|
pub destination: Ipv4Inet,
|
||||||
|
pub next_hop: Ipv4Addr,
|
||||||
|
pub metric: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Eq, PartialEq, Debug)]
|
||||||
|
pub struct RouteDataIpv6 {
|
||||||
|
pub destination: Ipv6Inet,
|
||||||
|
pub next_hop: Ipv6Addr,
|
||||||
|
pub metric: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts std::net::Ipv4Addr to winapi::shared::inaddr::in_addr
|
||||||
|
#[inline]
|
||||||
|
pub fn convert_ipv4addr_to_inaddr(ip: &Ipv4Addr) -> winapi::shared::inaddr::in_addr {
|
||||||
|
let mut winaddr = winapi::shared::inaddr::in_addr::default();
|
||||||
|
|
||||||
|
let s_un_b = unsafe { winaddr.S_un.S_un_b_mut() };
|
||||||
|
s_un_b.s_b1 = ip.octets()[0];
|
||||||
|
s_un_b.s_b2 = ip.octets()[1];
|
||||||
|
s_un_b.s_b3 = ip.octets()[2];
|
||||||
|
s_un_b.s_b4 = ip.octets()[3];
|
||||||
|
|
||||||
|
winaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts std::net::Ipv6Addr to winapi::shared::in6addr::in6_addr
|
||||||
|
#[inline]
|
||||||
|
pub fn convert_ipv6addr_to_inaddr(ip: &Ipv6Addr) -> winapi::shared::in6addr::in6_addr {
|
||||||
|
let mut winaddr = winapi::shared::in6addr::in6_addr::default();
|
||||||
|
let octets = ip.octets();
|
||||||
|
for i in 0..octets.len() {
|
||||||
|
unsafe { winaddr.u.Byte_mut()[i] = octets[i] };
|
||||||
|
}
|
||||||
|
|
||||||
|
winaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts std::net::Ipv4Addr to winapi::shared::ws2def::SOCKADDR_IN
|
||||||
|
pub fn convert_ipv4addr_to_sockaddr(ip: &Ipv4Addr) -> SOCKADDR_IN {
|
||||||
|
SOCKADDR_IN {
|
||||||
|
sin_family: AF_INET as ADDRESS_FAMILY,
|
||||||
|
sin_addr: convert_ipv4addr_to_inaddr(ip),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts ipnet::Ipv6Addr to winapi::shared::ws2ipdef::SOCKADDR_IN6
|
||||||
|
pub fn convert_ipv6addr_to_sockaddr(ip: &Ipv6Addr) -> SOCKADDR_IN6 {
|
||||||
|
SOCKADDR_IN6 {
|
||||||
|
sin6_family: AF_INET6 as ADDRESS_FAMILY,
|
||||||
|
sin6_addr: convert_ipv6addr_to_inaddr(ip),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts winapi::shared::ws2def::SOCKADDR_IN to std::net::Ipv4Addr
|
||||||
|
pub fn convert_sockaddr_to_ipv4addr(sockaddr: &SOCKADDR_IN) -> Ipv4Addr {
|
||||||
|
unsafe {
|
||||||
|
Ipv4Addr::new(
|
||||||
|
sockaddr.sin_addr.S_un.S_un_b().s_b1,
|
||||||
|
sockaddr.sin_addr.S_un.S_un_b().s_b2,
|
||||||
|
sockaddr.sin_addr.S_un.S_un_b().s_b3,
|
||||||
|
sockaddr.sin_addr.S_un.S_un_b().s_b4,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts a null-terminated Windows Unicode PWCHAR/LPWSTR to an OsString
|
||||||
|
pub fn u16_ptr_to_osstring(ptr: *const u16) -> OsString {
|
||||||
|
assert!(!ptr.is_null());
|
||||||
|
let len = (0..)
|
||||||
|
.take_while(|&i| unsafe { *ptr.offset(i) } != 0)
|
||||||
|
.count();
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
|
||||||
|
|
||||||
|
OsString::from_wide(slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function converts a null-terminated Windows PWCHAR/LPWSTR to a String
|
||||||
|
pub fn u16_ptr_to_string(ptr: *const u16) -> String {
|
||||||
|
assert!(!ptr.is_null());
|
||||||
|
let len = (0..)
|
||||||
|
.take_while(|&i| unsafe { *ptr.offset(i) } != 0)
|
||||||
|
.count();
|
||||||
|
let slice = unsafe { std::slice::from_raw_parts(ptr, len) };
|
||||||
|
|
||||||
|
String::from_utf16_lossy(slice)
|
||||||
|
}
|
||||||
@@ -1,61 +1,185 @@
|
|||||||
use std::{io, net::Ipv4Addr};
|
use crate::common::ifcfg::win::types::{RouteDataIpv4, RouteDataIpv6};
|
||||||
|
|
||||||
|
use super::win::luid::InterfaceLuid;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
|
use std::{
|
||||||
|
io,
|
||||||
|
net::{Ipv4Addr, Ipv6Addr},
|
||||||
|
ptr::null_mut,
|
||||||
|
};
|
||||||
|
use windows_sys::Win32::{
|
||||||
|
Foundation::NO_ERROR,
|
||||||
|
NetworkManagement::IpHelper::{GetIfEntry, SetIfEntry, MIB_IFROW},
|
||||||
|
System::Diagnostics::Debug::{
|
||||||
|
FormatMessageW, FORMAT_MESSAGE_FROM_SYSTEM, FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
},
|
||||||
|
};
|
||||||
use winreg::{
|
use winreg::{
|
||||||
enums::{HKEY_LOCAL_MACHINE, KEY_READ, KEY_WRITE},
|
enums::{HKEY_LOCAL_MACHINE, KEY_READ, KEY_WRITE},
|
||||||
RegKey,
|
RegKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{cidr_to_subnet_mask, run_shell_cmd, Error, IfConfiguerTrait};
|
use super::{Error, IfConfiguerTrait};
|
||||||
|
|
||||||
pub struct WindowsIfConfiger {}
|
pub struct WindowsIfConfiger {}
|
||||||
|
|
||||||
|
fn format_win_error(error: u32) -> String {
|
||||||
|
// use FormatMessageW to get the error message
|
||||||
|
let mut buffer = vec![0; 1024];
|
||||||
|
let size = buffer.len() as u32;
|
||||||
|
let flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
FormatMessageW(
|
||||||
|
flags,
|
||||||
|
null_mut(),
|
||||||
|
error,
|
||||||
|
0,
|
||||||
|
buffer.as_mut_ptr() as *mut u16,
|
||||||
|
size,
|
||||||
|
null_mut(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let str_end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len());
|
||||||
|
format!(
|
||||||
|
"{} (code: {})",
|
||||||
|
String::from_utf16_lossy(&buffer[..str_end])
|
||||||
|
.trim()
|
||||||
|
.to_string(),
|
||||||
|
error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl WindowsIfConfiger {
|
impl WindowsIfConfiger {
|
||||||
pub fn get_interface_index(name: &str) -> Option<u32> {
|
pub fn get_interface_index(name: &str) -> Option<u32> {
|
||||||
crate::arch::windows::find_interface_index(name).ok()
|
crate::arch::windows::find_interface_index(name).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list_ipv4(name: &str) -> Result<Vec<Ipv4Addr>, Error> {
|
#[tracing::instrument(err, ret)]
|
||||||
use anyhow::Context;
|
async fn add_ip_address(name: &str, addr: Ipv4Inet) -> Result<(), Error> {
|
||||||
use network_interface::NetworkInterfaceConfig;
|
let if_index = Self::get_interface_index(name).ok_or(Error::NotFound)?;
|
||||||
use std::net::IpAddr;
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
let ret = network_interface::NetworkInterface::show().with_context(|| "show interface")?;
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
let addrs = ret
|
})?;
|
||||||
.iter()
|
luid.add_ipv4_address(&addr)
|
||||||
.filter_map(|x| {
|
.map_err(|e| anyhow::anyhow!("Failed to add IPv4 address: {}", format_win_error(e)))?;
|
||||||
if x.name != name {
|
Ok(())
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
Some(x.addr.clone())
|
|
||||||
})
|
#[tracing::instrument(err, ret)]
|
||||||
.flat_map(|x| x)
|
async fn remove_ip_address(name: &str, addr: Option<Ipv4Inet>) -> Result<(), Error> {
|
||||||
.map(|x| x.ip())
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
.filter_map(|x| {
|
return Err(Error::NotFound);
|
||||||
if let IpAddr::V4(ipv4) = x {
|
};
|
||||||
Some(ipv4)
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
if let Some(addr) = addr {
|
||||||
|
luid.delete_ipv4_address(&addr).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to delete IPv4 address: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
} else {
|
} else {
|
||||||
None
|
luid.flush_ipv4_addresses().map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to flush IPv4 addresses: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
}
|
}
|
||||||
})
|
Ok(())
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Ok(addrs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_one_ipv4(name: &str, ip: Ipv4Addr) -> Result<(), Error> {
|
#[tracing::instrument(err, ret)]
|
||||||
run_shell_cmd(
|
async fn set_interface_status(name: &str, up: bool) -> Result<(), Error> {
|
||||||
format!(
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
"netsh interface ipv4 delete address {} address={}",
|
return Err(Error::NotFound);
|
||||||
name,
|
};
|
||||||
ip.to_string()
|
|
||||||
)
|
unsafe {
|
||||||
.as_str(),
|
let mut if_row = MIB_IFROW {
|
||||||
)
|
wszName: [0; 256],
|
||||||
.await
|
dwIndex: if_index,
|
||||||
|
dwType: 0,
|
||||||
|
dwMtu: 0,
|
||||||
|
dwSpeed: 0,
|
||||||
|
dwPhysAddrLen: 0,
|
||||||
|
bPhysAddr: [0; 8],
|
||||||
|
dwAdminStatus: if up { 1 } else { 2 }, // 1 = up, 2 = down
|
||||||
|
dwOperStatus: 0,
|
||||||
|
dwLastChange: 0,
|
||||||
|
dwInOctets: 0,
|
||||||
|
dwInUcastPkts: 0,
|
||||||
|
dwInNUcastPkts: 0,
|
||||||
|
dwInDiscards: 0,
|
||||||
|
dwInErrors: 0,
|
||||||
|
dwInUnknownProtos: 0,
|
||||||
|
dwOutOctets: 0,
|
||||||
|
dwOutUcastPkts: 0,
|
||||||
|
dwOutNUcastPkts: 0,
|
||||||
|
dwOutDiscards: 0,
|
||||||
|
dwOutErrors: 0,
|
||||||
|
dwOutQLen: 0,
|
||||||
|
dwDescrLen: 0,
|
||||||
|
bDescr: [0; 256],
|
||||||
|
};
|
||||||
|
|
||||||
|
if GetIfEntry(&mut if_row) == NO_ERROR {
|
||||||
|
if SetIfEntry(&if_row) == NO_ERROR {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Failed to set interface status").into())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Failed to get interface entry").into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(err, ret)]
|
||||||
|
async fn set_interface_mtu(name: &str, mtu: u32) -> Result<(), Error> {
|
||||||
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
};
|
||||||
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
luid.set_iface_config(mtu).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to set interface config: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(err, ret)]
|
||||||
|
async fn add_ipv6_address(name: &str, addr: Ipv6Inet) -> Result<(), Error> {
|
||||||
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
};
|
||||||
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
luid.add_ipv6_address(&addr)
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to add IPv6 address: {}", format_win_error(e)))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(err, ret)]
|
||||||
|
async fn remove_ipv6_address(name: &str, addr: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||||
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
|
return Err(Error::NotFound);
|
||||||
|
};
|
||||||
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
if let Some(addr) = addr {
|
||||||
|
luid.delete_ipv6_address(&addr).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to delete IPv6 address: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
luid.flush_ipv6_addresses().map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to flush IPv6 addresses: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl IfConfiguerTrait for WindowsIfConfiger {
|
impl IfConfiguerTrait for WindowsIfConfiger {
|
||||||
async fn add_ipv4_route(
|
async fn add_ipv4_route(
|
||||||
@@ -65,20 +189,20 @@ impl IfConfiguerTrait for WindowsIfConfiger {
|
|||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
cost: Option<i32>,
|
cost: Option<i32>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(idx) = Self::get_interface_index(name) else {
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
};
|
};
|
||||||
run_shell_cmd(
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
format!(
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
"route ADD {} MASK {} 10.1.1.1 IF {} METRIC {}",
|
})?;
|
||||||
address,
|
|
||||||
cidr_to_subnet_mask(cidr_prefix),
|
luid.add_routes_ipv4([RouteDataIpv4 {
|
||||||
idx,
|
destination: Ipv4Inet::new(address, cidr_prefix).unwrap(),
|
||||||
cost.unwrap_or(9000)
|
next_hop: Ipv4Addr::UNSPECIFIED,
|
||||||
)
|
metric: cost.unwrap_or(9000) as u32,
|
||||||
.as_str(),
|
}])
|
||||||
)
|
.map_err(|e| anyhow::anyhow!("Failed to add route: {}", format_win_error(e)))?;
|
||||||
.await
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv4_route(
|
async fn remove_ipv4_route(
|
||||||
@@ -87,19 +211,18 @@ impl IfConfiguerTrait for WindowsIfConfiger {
|
|||||||
address: Ipv4Addr,
|
address: Ipv4Addr,
|
||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(idx) = Self::get_interface_index(name) else {
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
};
|
};
|
||||||
run_shell_cmd(
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
format!(
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
"route DELETE {} MASK {} IF {}",
|
})?;
|
||||||
address,
|
luid.delete_route_ipv4(
|
||||||
cidr_to_subnet_mask(cidr_prefix),
|
&Ipv4Inet::new(address, cidr_prefix).unwrap(),
|
||||||
idx
|
&Ipv4Addr::UNSPECIFIED,
|
||||||
)
|
)
|
||||||
.as_str(),
|
.map_err(|e| anyhow::anyhow!("Failed to delete route: {}", format_win_error(e)))?;
|
||||||
)
|
Ok(())
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_ipv4_ip(
|
async fn add_ipv4_ip(
|
||||||
@@ -108,39 +231,15 @@ impl IfConfiguerTrait for WindowsIfConfiger {
|
|||||||
address: Ipv4Addr,
|
address: Ipv4Addr,
|
||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
run_shell_cmd(
|
Self::add_ip_address(name, Ipv4Inet::new(address, cidr_prefix).unwrap()).await
|
||||||
format!(
|
|
||||||
"netsh interface ipv4 add address {} address={} mask={}",
|
|
||||||
name,
|
|
||||||
address,
|
|
||||||
cidr_to_subnet_mask(cidr_prefix)
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_link_status(&self, name: &str, up: bool) -> Result<(), Error> {
|
async fn set_link_status(&self, name: &str, up: bool) -> Result<(), Error> {
|
||||||
run_shell_cmd(
|
Self::set_interface_status(name, up).await
|
||||||
format!(
|
|
||||||
"netsh interface set interface {} {}",
|
|
||||||
name,
|
|
||||||
if up { "enable" } else { "disable" }
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Addr>) -> Result<(), Error> {
|
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Inet>) -> Result<(), Error> {
|
||||||
if ip.is_none() {
|
Self::remove_ip_address(name, ip).await
|
||||||
for ip in Self::list_ipv4(name).await?.iter() {
|
|
||||||
Self::remove_one_ipv4(name, *ip).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Self::remove_one_ipv4(name, ip.unwrap()).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn wait_interface_show(&self, name: &str) -> Result<(), Error> {
|
async fn wait_interface_show(&self, name: &str) -> Result<(), Error> {
|
||||||
@@ -160,82 +259,66 @@ impl IfConfiguerTrait for WindowsIfConfiger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn set_mtu(&self, name: &str, mtu: u32) -> Result<(), Error> {
|
async fn set_mtu(&self, name: &str, mtu: u32) -> Result<(), Error> {
|
||||||
let _ = run_shell_cmd(
|
Self::set_interface_mtu(name, mtu).await
|
||||||
format!("netsh interface ipv6 set subinterface {} mtu={}", name, mtu).as_str(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
run_shell_cmd(
|
|
||||||
format!("netsh interface ipv4 set subinterface {} mtu={}", name, mtu).as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_ipv6_ip(
|
async fn add_ipv6_ip(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
address: std::net::Ipv6Addr,
|
address: Ipv6Addr,
|
||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
run_shell_cmd(
|
Self::add_ipv6_address(name, Ipv6Inet::new(address, cidr_prefix).unwrap()).await
|
||||||
format!(
|
|
||||||
"netsh interface ipv6 add address {} {}/{}",
|
|
||||||
name, address, cidr_prefix
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv6(&self, name: &str, ip: Option<std::net::Ipv6Addr>) -> Result<(), Error> {
|
async fn remove_ipv6(&self, name: &str, ip: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||||
if let Some(ip) = ip {
|
Self::remove_ipv6_address(name, ip).await
|
||||||
run_shell_cmd(
|
|
||||||
format!("netsh interface ipv6 delete address {} {}", name, ip).as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
// Remove all IPv6 addresses
|
|
||||||
run_shell_cmd(
|
|
||||||
format!("netsh interface ipv6 delete address {} all", name).as_str(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_ipv6_route(
|
async fn add_ipv6_route(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
address: std::net::Ipv6Addr,
|
address: Ipv6Addr,
|
||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
cost: Option<i32>,
|
cost: Option<i32>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let cmd = if let Some(cost) = cost {
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
format!(
|
return Err(Error::NotFound);
|
||||||
"netsh interface ipv6 add route {}/{} {} metric={}",
|
|
||||||
address, cidr_prefix, name, cost
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
format!(
|
|
||||||
"netsh interface ipv6 add route {}/{} {}",
|
|
||||||
address, cidr_prefix, name
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
run_shell_cmd(cmd.as_str()).await
|
|
||||||
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
luid.add_routes_ipv6([RouteDataIpv6 {
|
||||||
|
destination: Ipv6Inet::new(address, cidr_prefix).unwrap(),
|
||||||
|
next_hop: Ipv6Addr::UNSPECIFIED,
|
||||||
|
metric: cost.unwrap_or(9000) as u32,
|
||||||
|
}])
|
||||||
|
.map_err(|e| anyhow::anyhow!("Failed to add route: {}", format_win_error(e)))?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_ipv6_route(
|
async fn remove_ipv6_route(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
address: std::net::Ipv6Addr,
|
address: Ipv6Addr,
|
||||||
cidr_prefix: u8,
|
cidr_prefix: u8,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
run_shell_cmd(
|
let Some(if_index) = Self::get_interface_index(name) else {
|
||||||
format!(
|
return Err(Error::NotFound);
|
||||||
"netsh interface ipv6 delete route {}/{} {}",
|
};
|
||||||
address, cidr_prefix, name
|
|
||||||
|
let luid = InterfaceLuid::luid_from_index(if_index).map_err(|e| {
|
||||||
|
anyhow::anyhow!("Failed to get interface luid: {}", format_win_error(e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
luid.delete_route_ipv6(
|
||||||
|
&Ipv6Inet::new(address, cidr_prefix).unwrap(),
|
||||||
|
&Ipv6Addr::UNSPECIFIED,
|
||||||
)
|
)
|
||||||
.as_str(),
|
.map_err(|e| anyhow::anyhow!("Failed to delete route: {}", format_win_error(e)))?;
|
||||||
)
|
Ok(())
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -800,6 +800,7 @@ impl NetworkOptions {
|
|||||||
if let Some(dev_name) = &self.dev_name {
|
if let Some(dev_name) = &self.dev_name {
|
||||||
f.dev_name = dev_name.clone()
|
f.dev_name = dev_name.clone()
|
||||||
}
|
}
|
||||||
|
println!("mtu: {}, {:?}", f.mtu, self.mtu);
|
||||||
if let Some(mtu) = self.mtu {
|
if let Some(mtu) = self.mtu {
|
||||||
f.mtu = mtu as u32;
|
f.mtu = mtu as u32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ use crate::{
|
|||||||
|
|
||||||
use byteorder::WriteBytesExt as _;
|
use byteorder::WriteBytesExt as _;
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
|
use cidr::{Ipv4Inet, Ipv6Inet};
|
||||||
use futures::{lock::BiLock, ready, SinkExt, Stream, StreamExt};
|
use futures::{lock::BiLock, ready, SinkExt, Stream, StreamExt};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use pnet::packet::{ipv4::Ipv4Packet, ipv6::Ipv6Packet};
|
use pnet::packet::{ipv4::Ipv4Packet, ipv6::Ipv6Packet};
|
||||||
@@ -442,13 +443,13 @@ impl VirtualNic {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_ip(&self, ip: Option<Ipv4Addr>) -> Result<(), Error> {
|
pub async fn remove_ip(&self, ip: Option<Ipv4Inet>) -> Result<(), Error> {
|
||||||
let _g = self.global_ctx.net_ns.guard();
|
let _g = self.global_ctx.net_ns.guard();
|
||||||
self.ifcfg.remove_ip(self.ifname(), ip).await?;
|
self.ifcfg.remove_ip(self.ifname(), ip).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_ipv6(&self, ip: Option<Ipv6Addr>) -> Result<(), Error> {
|
pub async fn remove_ipv6(&self, ip: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||||
let _g = self.global_ctx.net_ns.guard();
|
let _g = self.global_ctx.net_ns.guard();
|
||||||
self.ifcfg.remove_ipv6(self.ifname(), ip).await?;
|
self.ifcfg.remove_ipv6(self.ifname(), ip).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user