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:
DavHau
2025-07-04 22:43:30 +07:00
committed by GitHub
parent 01e491ec07
commit d0cfc49806
32 changed files with 893 additions and 70 deletions

View File

@@ -1,6 +1,6 @@
use std::{
fmt::Debug,
net::Ipv4Addr,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
sync::{Arc, Weak},
time::{Instant, SystemTime},
};
@@ -873,6 +873,43 @@ impl PeerManager {
(dst_peers, is_exit_node)
}
pub async fn get_msg_dst_peer_ipv6(&self, ipv6_addr: &Ipv6Addr) -> (Vec<PeerId>, bool) {
let mut is_exit_node = false;
let mut dst_peers = vec![];
let network_length = self
.global_ctx
.get_ipv6()
.map(|x| x.network_length())
.unwrap_or(64);
let ipv6_inet = cidr::Ipv6Inet::new(*ipv6_addr, network_length).unwrap();
if ipv6_addr.is_multicast() || *ipv6_addr == ipv6_inet.last_address() {
dst_peers.extend(
self.peers
.list_routes()
.await
.iter()
.map(|x| x.key().clone()),
);
} else if let Some(peer_id) = self.peers.get_peer_id_by_ipv6(&ipv6_addr).await {
dst_peers.push(peer_id);
} else {
// For IPv6, we'll need to implement exit node support later
// For now, just try to find any available peer for routing
if dst_peers.is_empty() {
dst_peers.extend(
self.peers
.list_routes()
.await
.iter()
.map(|x| x.key().clone()),
);
is_exit_node = true;
}
}
(dst_peers, is_exit_node)
}
pub async fn try_compress_and_encrypt(
compress_algo: CompressorAlgo,
encryptor: &Box<dyn Encryptor>,
@@ -887,11 +924,11 @@ impl PeerManager {
Ok(())
}
pub async fn send_msg_ipv4(&self, mut msg: ZCPacket, ipv4_addr: Ipv4Addr) -> Result<(), Error> {
pub async fn send_msg_by_ip(&self, mut msg: ZCPacket, ip_addr: IpAddr) -> Result<(), Error> {
tracing::trace!(
"do send_msg in peer manager, msg: {:?}, ipv4_addr: {}",
"do send_msg in peer manager, msg: {:?}, ip_addr: {}",
msg,
ipv4_addr
ip_addr
);
msg.fill_peer_manager_hdr(
@@ -911,10 +948,13 @@ impl PeerManager {
.await;
}
let (dst_peers, is_exit_node) = self.get_msg_dst_peer(&ipv4_addr).await;
let (dst_peers, is_exit_node) = match ip_addr {
IpAddr::V4(ipv4_addr) => self.get_msg_dst_peer(&ipv4_addr).await,
IpAddr::V6(ipv6_addr) => self.get_msg_dst_peer_ipv6(&ipv6_addr).await,
};
if dst_peers.is_empty() {
tracing::info!("no peer id for ipv4: {}", ipv4_addr);
tracing::info!("no peer id for ip: {}", ip_addr);
return Ok(());
}

View File

@@ -1,4 +1,4 @@
use std::{net::Ipv4Addr, sync::Arc};
use std::{net::{Ipv4Addr, Ipv6Addr}, sync::Arc};
use anyhow::Context;
use dashmap::DashMap;
@@ -194,6 +194,16 @@ impl PeerMap {
None
}
pub async fn get_peer_id_by_ipv6(&self, ipv6: &Ipv6Addr) -> Option<PeerId> {
for route in self.routes.read().await.iter() {
let peer_id = route.get_peer_id_by_ipv6(ipv6).await;
if peer_id.is_some() {
return peer_id;
}
}
None
}
pub async fn get_route_peer_info(&self, peer_id: PeerId) -> Option<RoutePeerInfo> {
for route in self.routes.read().await.iter() {
if let Some(info) = route.get_peer_info(peer_id).await {

View File

@@ -1,7 +1,7 @@
use std::{
collections::BTreeSet,
fmt::Debug,
net::Ipv4Addr,
net::{Ipv4Addr, Ipv6Addr},
sync::{
atomic::{AtomicBool, AtomicU32, Ordering},
Arc, Weak,
@@ -125,6 +125,7 @@ impl RoutePeerInfo {
peer_route_id: 0,
network_length: 24,
quic_port: None,
ipv6_addr: None,
}
}
@@ -165,6 +166,7 @@ impl RoutePeerInfo {
.unwrap_or(24),
quic_port: global_ctx.get_quic_proxy_port().map(|x| x as u32),
ipv6_addr: global_ctx.get_ipv6().map(|x| x.into()),
};
let need_update_periodically = if let Ok(Ok(d)) =
@@ -221,6 +223,8 @@ impl Into<crate::proto::cli::Route> for RoutePeerInfo {
next_hop_peer_id_latency_first: None,
cost_latency_first: None,
path_latency_latency_first: None,
ipv6_addr: self.ipv6_addr.map(Into::into),
}
}
}
@@ -635,6 +639,7 @@ struct RouteTable {
peer_infos: DashMap<PeerId, RoutePeerInfo>,
next_hop_map: NextHopMap,
ipv4_peer_id_map: DashMap<Ipv4Addr, PeerId>,
ipv6_peer_id_map: DashMap<Ipv6Addr, PeerId>,
cidr_peer_id_map: DashMap<cidr::IpCidr, PeerId>,
next_hop_map_version: AtomicVersion,
}
@@ -645,6 +650,7 @@ impl RouteTable {
peer_infos: DashMap::new(),
next_hop_map: DashMap::new(),
ipv4_peer_id_map: DashMap::new(),
ipv6_peer_id_map: DashMap::new(),
cidr_peer_id_map: DashMap::new(),
next_hop_map_version: AtomicVersion::new(),
}
@@ -742,6 +748,10 @@ impl RouteTable {
// remove ipv4 map for peers we cannot reach.
self.next_hop_map.contains_key(v)
});
self.ipv6_peer_id_map.retain(|_, v| {
// remove ipv6 map for peers we cannot reach.
self.next_hop_map.contains_key(v)
});
self.cidr_peer_id_map.retain(|_, v| {
// remove cidr map for peers we cannot reach.
self.next_hop_map.contains_key(v)
@@ -876,6 +886,17 @@ impl RouteTable {
.or_insert(*peer_id);
}
if let Some(ipv6_addr) = info.ipv6_addr.and_then(|x| x.address) {
self.ipv6_peer_id_map
.entry(ipv6_addr.into())
.and_modify(|v| {
if *v != *peer_id && is_new_peer_better(*v) {
*v = *peer_id;
}
})
.or_insert(*peer_id);
}
for cidr in info.proxy_cidrs.iter() {
self.cidr_peer_id_map
.entry(cidr.parse().unwrap())
@@ -2267,6 +2288,21 @@ impl Route for PeerRoute {
None
}
async fn get_peer_id_by_ipv6(&self, ipv6_addr: &Ipv6Addr) -> Option<PeerId> {
let route_table = &self.service_impl.route_table;
if let Some(peer_id) = route_table.ipv6_peer_id_map.get(ipv6_addr) {
return Some(*peer_id);
}
// TODO: Add proxy support for IPv6 similar to IPv4
// if let Some(peer_id) = route_table.get_peer_id_for_proxy_ipv6(ipv6_addr) {
// return Some(peer_id);
// }
tracing::debug!(?ipv6_addr, "no peer id for ipv6");
None
}
async fn set_route_cost_fn(&self, _cost_fn: RouteCostCalculator) {
*self.service_impl.cost_calculator.write().unwrap() = Some(_cost_fn);
self.service_impl.synced_route_info.version.inc();

View File

@@ -36,6 +36,11 @@ impl DirectConnectorRpc for DirectConnectorManagerRpcServer {
.chain(self.global_ctx.get_running_listeners().into_iter())
.map(Into::into)
.collect();
// remove et ipv6 from the interface ipv6 list
if let Some(et_ipv6) = self.global_ctx.get_ipv6() {
let et_ipv6: crate::proto::common::Ipv6Addr = et_ipv6.address().into();
ret.interface_ipv6s.retain(|x| *x != et_ipv6);
}
tracing::trace!(
"get_ip_list: public_ipv4: {:?}, public_ipv6: {:?}, listeners: {:?}",
ret.public_ipv4,

View File

@@ -1,4 +1,4 @@
use std::{net::Ipv4Addr, sync::Arc};
use std::{net::{Ipv4Addr, Ipv6Addr}, sync::Arc};
use dashmap::DashMap;
@@ -82,6 +82,10 @@ pub trait Route {
None
}
async fn get_peer_id_by_ipv6(&self, _ipv6: &Ipv6Addr) -> Option<PeerId> {
None
}
async fn list_peers_own_foreign_network(
&self,
_network_identity: &NetworkIdentity,