mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-09 11:14:30 +00:00
Add support for IPv6 within VPN (#1061)
* add flake.nix with nix based dev shell * add support for IPv6 * update thunk --------- Co-authored-by: sijie.sun <sijie.sun@smartx.com>
This commit is contained in:
@@ -34,7 +34,7 @@ pub async fn prepare_env(dns_name: &str, tun_ip: Ipv4Inet) -> (Arc<PeerManager>,
|
||||
|
||||
let r = Arc::new(tokio::sync::Mutex::new(r));
|
||||
let mut virtual_nic = NicCtx::new(peer_mgr.get_global_ctx(), &peer_mgr, r);
|
||||
virtual_nic.run(tun_ip).await.unwrap();
|
||||
virtual_nic.run(Some(tun_ip), None).await.unwrap();
|
||||
|
||||
(peer_mgr, virtual_nic)
|
||||
}
|
||||
|
||||
@@ -484,7 +484,7 @@ impl Instance {
|
||||
&peer_manager_c,
|
||||
_peer_packet_receiver.clone(),
|
||||
);
|
||||
if let Err(e) = new_nic_ctx.run(ip).await {
|
||||
if let Err(e) = new_nic_ctx.run(Some(ip), global_ctx_c.get_ipv6()).await {
|
||||
tracing::error!(
|
||||
?current_dhcp_ip,
|
||||
?candidate_ipv4_addr,
|
||||
@@ -532,24 +532,29 @@ impl Instance {
|
||||
|
||||
if !self.global_ctx.config.get_flags().no_tun {
|
||||
#[cfg(not(any(target_os = "android", target_env = "ohos")))]
|
||||
if let Some(ipv4_addr) = self.global_ctx.get_ipv4() {
|
||||
let mut new_nic_ctx = NicCtx::new(
|
||||
self.global_ctx.clone(),
|
||||
&self.peer_manager,
|
||||
self.peer_packet_receiver.clone(),
|
||||
);
|
||||
new_nic_ctx.run(ipv4_addr).await?;
|
||||
let ifname = new_nic_ctx.ifname().await;
|
||||
Self::use_new_nic_ctx(
|
||||
self.nic_ctx.clone(),
|
||||
new_nic_ctx,
|
||||
Self::create_magic_dns_runner(
|
||||
self.peer_manager.clone(),
|
||||
ifname,
|
||||
ipv4_addr.clone(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
{
|
||||
let ipv4_addr = self.global_ctx.get_ipv4();
|
||||
let ipv6_addr = self.global_ctx.get_ipv6();
|
||||
|
||||
// Only run if we have at least one IP address (IPv4 or IPv6)
|
||||
if ipv4_addr.is_some() || ipv6_addr.is_some() {
|
||||
let mut new_nic_ctx = NicCtx::new(
|
||||
self.global_ctx.clone(),
|
||||
&self.peer_manager,
|
||||
self.peer_packet_receiver.clone(),
|
||||
);
|
||||
|
||||
new_nic_ctx.run(ipv4_addr, ipv6_addr).await?;
|
||||
let ifname = new_nic_ctx.ifname().await;
|
||||
|
||||
// Create Magic DNS runner only if we have IPv4
|
||||
let dns_runner = if let Some(ipv4) = ipv4_addr {
|
||||
Self::create_magic_dns_runner(self.peer_manager.clone(), ifname, ipv4)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Self::use_new_nic_ctx(self.nic_ctx.clone(), new_nic_ctx, dns_runner).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -852,7 +857,7 @@ impl Drop for Instance {
|
||||
};
|
||||
|
||||
let now = std::time::Instant::now();
|
||||
while now.elapsed().as_secs() < 1 {
|
||||
while now.elapsed().as_secs() < 10 {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
if pm.strong_count() == 0 {
|
||||
tracing::info!(
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
io,
|
||||
net::Ipv4Addr,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
pin::Pin,
|
||||
sync::{Arc, Weak},
|
||||
task::{Context, Poll},
|
||||
@@ -25,7 +25,7 @@ use byteorder::WriteBytesExt as _;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{lock::BiLock, ready, SinkExt, Stream, StreamExt};
|
||||
use pin_project_lite::pin_project;
|
||||
use pnet::packet::ipv4::Ipv4Packet;
|
||||
use pnet::packet::{ipv4::Ipv4Packet, ipv6::Ipv6Packet};
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite, ReadBuf},
|
||||
sync::Mutex,
|
||||
@@ -434,12 +434,26 @@ impl VirtualNic {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_route(&self, address: Ipv6Addr, cidr: u8) -> Result<(), Error> {
|
||||
let _g = self.global_ctx.net_ns.guard();
|
||||
self.ifcfg
|
||||
.add_ipv6_route(self.ifname(), address, cidr, None)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_ip(&self, ip: Option<Ipv4Addr>) -> Result<(), Error> {
|
||||
let _g = self.global_ctx.net_ns.guard();
|
||||
self.ifcfg.remove_ip(self.ifname(), ip).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6(&self, ip: Option<Ipv6Addr>) -> Result<(), Error> {
|
||||
let _g = self.global_ctx.net_ns.guard();
|
||||
self.ifcfg.remove_ipv6(self.ifname(), ip).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_ip(&self, ip: Ipv4Addr, cidr: i32) -> Result<(), Error> {
|
||||
let _g = self.global_ctx.net_ns.guard();
|
||||
self.ifcfg
|
||||
@@ -448,6 +462,14 @@ impl VirtualNic {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_ipv6(&self, ip: Ipv6Addr, cidr: i32) -> Result<(), Error> {
|
||||
let _g = self.global_ctx.net_ns.guard();
|
||||
self.ifcfg
|
||||
.add_ipv6_ip(self.ifname(), ip, cidr as u8)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_ifcfg(&self) -> impl IfConfiguerTrait {
|
||||
IfConfiger {}
|
||||
}
|
||||
@@ -496,6 +518,20 @@ impl NicCtx {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn assign_ipv6_to_tun_device(&self, ipv6_addr: cidr::Ipv6Inet) -> Result<(), Error> {
|
||||
let nic = self.nic.lock().await;
|
||||
nic.link_up().await?;
|
||||
nic.remove_ipv6(None).await?;
|
||||
nic.add_ipv6(ipv6_addr.address(), ipv6_addr.network_length() as i32)
|
||||
.await?;
|
||||
#[cfg(any(target_os = "macos", target_os = "freebsd"))]
|
||||
{
|
||||
nic.add_ipv6_route(ipv6_addr.first_address(), ipv6_addr.network_length())
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn do_forward_nic_to_peers_ipv4(ret: ZCPacket, mgr: &PeerManager) {
|
||||
if let Some(ipv4) = Ipv4Packet::new(ret.payload()) {
|
||||
if ipv4.get_version() != 4 {
|
||||
@@ -509,16 +545,53 @@ impl NicCtx {
|
||||
);
|
||||
|
||||
// TODO: use zero-copy
|
||||
let send_ret = mgr.send_msg_ipv4(ret, dst_ipv4).await;
|
||||
let send_ret = mgr.send_msg_by_ip(ret, IpAddr::V4(dst_ipv4)).await;
|
||||
if send_ret.is_err() {
|
||||
tracing::trace!(?send_ret, "[USER_PACKET] send_msg_ipv4 failed")
|
||||
tracing::trace!(?send_ret, "[USER_PACKET] send_msg failed")
|
||||
}
|
||||
} else {
|
||||
tracing::warn!(?ret, "[USER_PACKET] not ipv4 packet");
|
||||
}
|
||||
}
|
||||
|
||||
fn do_forward_nic_to_peers(
|
||||
async fn do_forward_nic_to_peers_ipv6(ret: ZCPacket, mgr: &PeerManager) {
|
||||
if let Some(ipv6) = Ipv6Packet::new(ret.payload()) {
|
||||
if ipv6.get_version() != 6 {
|
||||
tracing::info!("[USER_PACKET] not ipv6 packet: {:?}", ipv6);
|
||||
return;
|
||||
}
|
||||
let dst_ipv6 = ipv6.get_destination();
|
||||
tracing::trace!(
|
||||
?ret,
|
||||
"[USER_PACKET] recv new packet from tun device and forward to peers."
|
||||
);
|
||||
|
||||
// TODO: use zero-copy
|
||||
let send_ret = mgr.send_msg_by_ip(ret, IpAddr::V6(dst_ipv6)).await;
|
||||
if send_ret.is_err() {
|
||||
tracing::trace!(?send_ret, "[USER_PACKET] send_msg failed")
|
||||
}
|
||||
} else {
|
||||
tracing::warn!(?ret, "[USER_PACKET] not ipv6 packet");
|
||||
}
|
||||
}
|
||||
|
||||
async fn do_forward_nic_to_peers(ret: ZCPacket, mgr: &PeerManager) {
|
||||
let payload = ret.payload();
|
||||
if payload.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
match payload[0] >> 4 {
|
||||
4 => Self::do_forward_nic_to_peers_ipv4(ret, mgr).await,
|
||||
6 => Self::do_forward_nic_to_peers_ipv6(ret, mgr).await,
|
||||
_ => {
|
||||
tracing::warn!(?ret, "[USER_PACKET] unknown IP version");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn do_forward_nic_to_peers_task(
|
||||
&mut self,
|
||||
mut stream: Pin<Box<dyn ZCPacketStream>>,
|
||||
) -> Result<(), Error> {
|
||||
@@ -532,7 +605,7 @@ impl NicCtx {
|
||||
tracing::error!("read from nic failed: {:?}", ret);
|
||||
break;
|
||||
}
|
||||
Self::do_forward_nic_to_peers_ipv4(ret.unwrap(), mgr.as_ref()).await;
|
||||
Self::do_forward_nic_to_peers(ret.unwrap(), mgr.as_ref()).await;
|
||||
}
|
||||
panic!("nic stream closed");
|
||||
});
|
||||
@@ -647,7 +720,7 @@ impl NicCtx {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn run(&mut self, ipv4_addr: cidr::Ipv4Inet) -> Result<(), Error> {
|
||||
pub async fn run(&mut self, ipv4_addr: Option<cidr::Ipv4Inet>, ipv6_addr: Option<cidr::Ipv6Inet>) -> Result<(), Error> {
|
||||
let tunnel = {
|
||||
let mut nic = self.nic.lock().await;
|
||||
match nic.create_dev().await {
|
||||
@@ -681,10 +754,19 @@ impl NicCtx {
|
||||
|
||||
let (stream, sink) = tunnel.split();
|
||||
|
||||
self.do_forward_nic_to_peers(stream)?;
|
||||
self.do_forward_nic_to_peers_task(stream)?;
|
||||
self.do_forward_peers_to_nic(sink);
|
||||
|
||||
self.assign_ipv4_to_tun_device(ipv4_addr).await?;
|
||||
// Assign IPv4 address if provided
|
||||
if let Some(ipv4_addr) = ipv4_addr {
|
||||
self.assign_ipv4_to_tun_device(ipv4_addr).await?;
|
||||
}
|
||||
|
||||
// Assign IPv6 address if provided
|
||||
if let Some(ipv6_addr) = ipv6_addr {
|
||||
self.assign_ipv6_to_tun_device(ipv6_addr).await?;
|
||||
}
|
||||
|
||||
self.run_proxy_cidrs_route_updater().await?;
|
||||
|
||||
Ok(())
|
||||
@@ -710,7 +792,7 @@ impl NicCtx {
|
||||
|
||||
let (stream, sink) = tunnel.split();
|
||||
|
||||
self.do_forward_nic_to_peers(stream)?;
|
||||
self.do_forward_nic_to_peers_task(stream)?;
|
||||
self.do_forward_peers_to_nic(sink);
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user