mirror of
				https://github.com/EasyTier/EasyTier.git
				synced 2025-10-31 20:12:53 +08:00 
			
		
		
		
	Add support for IPv6 within VPN (#1061)
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				EasyTier Core / pre_job (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build_web (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (windows-i686, windows-latest, i686-pc-windows-msvc) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / core-result (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Core / magisk_build (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / pre_job (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (windows-i686, i686-pc-windows-msvc, windows-latest, i686-pc-windows-msvc) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier GUI / gui-result (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Mobile / pre_job (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Mobile / mobile-result (push) Has been cancelled
				
			
		
			
				
	
				EasyTier OHOS / pre_job (push) Has been cancelled
				
			
		
			
				
	
				EasyTier OHOS / build-ohos (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Test / pre_job (push) Has been cancelled
				
			
		
			
				
	
				EasyTier Test / test (push) Has been cancelled
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	EasyTier Core / pre_job (push) Has been cancelled
				
			EasyTier Core / build_web (push) Has been cancelled
				
			EasyTier Core / build (freebsd-13.2-x86_64, 13.2, ubuntu-22.04, x86_64-unknown-freebsd) (push) Has been cancelled
				
			EasyTier Core / build (linux-aarch64, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
				
			EasyTier Core / build (linux-arm, ubuntu-22.04, arm-unknown-linux-musleabi) (push) Has been cancelled
				
			EasyTier Core / build (linux-armhf, ubuntu-22.04, arm-unknown-linux-musleabihf) (push) Has been cancelled
				
			EasyTier Core / build (linux-armv7, ubuntu-22.04, armv7-unknown-linux-musleabi) (push) Has been cancelled
				
			EasyTier Core / build (linux-armv7hf, ubuntu-22.04, armv7-unknown-linux-musleabihf) (push) Has been cancelled
				
			EasyTier Core / build (linux-mips, ubuntu-22.04, mips-unknown-linux-musl) (push) Has been cancelled
				
			EasyTier Core / build (linux-mipsel, ubuntu-22.04, mipsel-unknown-linux-musl) (push) Has been cancelled
				
			EasyTier Core / build (linux-x86_64, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
				
			EasyTier Core / build (macos-aarch64, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
				
			EasyTier Core / build (macos-x86_64, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
				
			EasyTier Core / build (windows-arm64, windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
				
			EasyTier Core / build (windows-i686, windows-latest, i686-pc-windows-msvc) (push) Has been cancelled
				
			EasyTier Core / build (windows-x86_64, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
				
			EasyTier Core / core-result (push) Has been cancelled
				
			EasyTier Core / magisk_build (push) Has been cancelled
				
			EasyTier GUI / pre_job (push) Has been cancelled
				
			EasyTier GUI / build-gui (linux-aarch64, aarch64-unknown-linux-gnu, ubuntu-22.04, aarch64-unknown-linux-musl) (push) Has been cancelled
				
			EasyTier GUI / build-gui (linux-x86_64, x86_64-unknown-linux-gnu, ubuntu-22.04, x86_64-unknown-linux-musl) (push) Has been cancelled
				
			EasyTier GUI / build-gui (macos-aarch64, aarch64-apple-darwin, macos-latest, aarch64-apple-darwin) (push) Has been cancelled
				
			EasyTier GUI / build-gui (macos-x86_64, x86_64-apple-darwin, macos-latest, x86_64-apple-darwin) (push) Has been cancelled
				
			EasyTier GUI / build-gui (windows-arm64, aarch64-pc-windows-msvc, windows-latest, aarch64-pc-windows-msvc) (push) Has been cancelled
				
			EasyTier GUI / build-gui (windows-i686, i686-pc-windows-msvc, windows-latest, i686-pc-windows-msvc) (push) Has been cancelled
				
			EasyTier GUI / build-gui (windows-x86_64, x86_64-pc-windows-msvc, windows-latest, x86_64-pc-windows-msvc) (push) Has been cancelled
				
			EasyTier GUI / gui-result (push) Has been cancelled
				
			EasyTier Mobile / pre_job (push) Has been cancelled
				
			EasyTier Mobile / build-mobile (android, ubuntu-22.04, android) (push) Has been cancelled
				
			EasyTier Mobile / mobile-result (push) Has been cancelled
				
			EasyTier OHOS / pre_job (push) Has been cancelled
				
			EasyTier OHOS / build-ohos (push) Has been cancelled
				
			EasyTier Test / pre_job (push) Has been cancelled
				
			EasyTier Test / test (push) Has been cancelled
				
			* 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:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -38,3 +38,5 @@ node_modules | ||||
|  | ||||
| easytier-gui/src-tauri/*.dll | ||||
| /easytier-contrib/easytier-ohrs/dist/ | ||||
|  | ||||
| .direnv | ||||
|   | ||||
							
								
								
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -8606,8 +8606,8 @@ dependencies = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "thunk-rs" | ||||
| version = "0.3.3" | ||||
| source = "git+https://github.com/easytier/thunk.git#5e8371a3100dbc18dda952a2036c6bd6fb0504db" | ||||
| version = "0.3.4" | ||||
| source = "git+https://github.com/easytier/thunk.git#403f0d26d3d5bcfdfd76c23e36e517f19fe891e0" | ||||
|  | ||||
| [[package]] | ||||
| name = "tiff" | ||||
|   | ||||
| @@ -27,6 +27,9 @@ core_clap: | ||||
|   ipv4: | ||||
|     en: "ipv4 address of this vpn node, if empty, this node will only forward packets and no TUN device will be created" | ||||
|     zh-CN: "此VPN节点的IPv4地址,如果为空,则此节点将仅转发数据包,不会创建TUN设备" | ||||
|   ipv6: | ||||
|     en: "ipv6 address of this vpn node, can be used together with ipv4 for dual-stack operation" | ||||
|     zh-CN: "此VPN节点的IPv6地址,可与IPv4一起使用以进行双栈操作" | ||||
|   dhcp: | ||||
|     en: "automatically determine and set IP address by Easytier, and the IP address starts from 10.0.0.1 by default. Warning, if there is an IP conflict in the network when using DHCP, the IP will be automatically changed." | ||||
|     zh-CN: "由Easytier自动确定并设置IP地址,默认从10.0.0.1开始。警告:在使用DHCP时,如果网络中出现IP冲突,IP将自动更改。" | ||||
|   | ||||
| @@ -64,6 +64,9 @@ pub trait ConfigLoader: Send + Sync { | ||||
|     fn get_ipv4(&self) -> Option<cidr::Ipv4Inet>; | ||||
|     fn set_ipv4(&self, addr: Option<cidr::Ipv4Inet>); | ||||
|  | ||||
|     fn get_ipv6(&self) -> Option<cidr::Ipv6Inet>; | ||||
|     fn set_ipv6(&self, addr: Option<cidr::Ipv6Inet>); | ||||
|  | ||||
|     fn get_dhcp(&self) -> bool; | ||||
|     fn set_dhcp(&self, dhcp: bool); | ||||
|  | ||||
| @@ -259,6 +262,7 @@ struct Config { | ||||
|     instance_name: Option<String>, | ||||
|     instance_id: Option<uuid::Uuid>, | ||||
|     ipv4: Option<String>, | ||||
|     ipv6: Option<String>, | ||||
|     dhcp: Option<bool>, | ||||
|     network_identity: Option<NetworkIdentity>, | ||||
|     listeners: Option<Vec<url::Url>>, | ||||
| @@ -416,6 +420,23 @@ impl ConfigLoader for TomlConfigLoader { | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     fn get_ipv6(&self) -> Option<cidr::Ipv6Inet> { | ||||
|         let locked_config = self.config.lock().unwrap(); | ||||
|         locked_config | ||||
|             .ipv6 | ||||
|             .as_ref() | ||||
|             .map(|s| s.parse().ok()) | ||||
|             .flatten() | ||||
|     } | ||||
|  | ||||
|     fn set_ipv6(&self, addr: Option<cidr::Ipv6Inet>) { | ||||
|         self.config.lock().unwrap().ipv6 = if let Some(addr) = addr { | ||||
|             Some(addr.to_string()) | ||||
|         } else { | ||||
|             None | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     fn get_dhcp(&self) -> bool { | ||||
|         self.config.lock().unwrap().dhcp.unwrap_or_default() | ||||
|     } | ||||
|   | ||||
| @@ -61,6 +61,7 @@ pub struct GlobalCtx { | ||||
|     event_bus: EventBus, | ||||
|  | ||||
|     cached_ipv4: AtomicCell<Option<cidr::Ipv4Inet>>, | ||||
|     cached_ipv6: AtomicCell<Option<cidr::Ipv6Inet>>, | ||||
|     cached_proxy_cidrs: AtomicCell<Option<Vec<ProxyNetworkConfig>>>, | ||||
|  | ||||
|     ip_collector: Mutex<Option<Arc<IPCollector>>>, | ||||
| @@ -124,6 +125,7 @@ impl GlobalCtx { | ||||
|  | ||||
|             event_bus, | ||||
|             cached_ipv4: AtomicCell::new(None), | ||||
|             cached_ipv6: AtomicCell::new(None), | ||||
|             cached_proxy_cidrs: AtomicCell::new(None), | ||||
|  | ||||
|             ip_collector: Mutex::new(Some(Arc::new(IPCollector::new( | ||||
| @@ -191,6 +193,20 @@ impl GlobalCtx { | ||||
|         self.cached_ipv4.store(None); | ||||
|     } | ||||
|  | ||||
|     pub fn get_ipv6(&self) -> Option<cidr::Ipv6Inet> { | ||||
|         if let Some(ret) = self.cached_ipv6.load() { | ||||
|             return Some(ret); | ||||
|         } | ||||
|         let addr = self.config.get_ipv6(); | ||||
|         self.cached_ipv6.store(addr.clone()); | ||||
|         return addr; | ||||
|     } | ||||
|  | ||||
|     pub fn set_ipv6(&self, addr: Option<cidr::Ipv6Inet>) { | ||||
|         self.config.set_ipv6(addr); | ||||
|         self.cached_ipv6.store(None); | ||||
|     } | ||||
|  | ||||
|     pub fn get_id(&self) -> uuid::Uuid { | ||||
|         self.config.get_id() | ||||
|     } | ||||
|   | ||||
| @@ -80,4 +80,62 @@ impl IfConfiguerTrait for MacIfConfiger { | ||||
|     async fn set_mtu(&self, name: &str, mtu: u32) -> Result<(), Error> { | ||||
|         run_shell_cmd(format!("ifconfig {} mtu {}", name, mtu).as_str()).await | ||||
|     } | ||||
|  | ||||
|     async fn add_ipv6_ip( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         run_shell_cmd( | ||||
|             format!("ifconfig {} inet6 {}/{} add", name, address, cidr_prefix).as_str(), | ||||
|         ) | ||||
|         .await | ||||
|     } | ||||
|  | ||||
|     async fn remove_ipv6(&self, name: &str, ip: Option<std::net::Ipv6Addr>) -> Result<(), Error> { | ||||
|         if let Some(ip) = ip { | ||||
|             run_shell_cmd(format!("ifconfig {} inet6 {} delete", name, ip).as_str()).await | ||||
|         } else { | ||||
|             // Remove all IPv6 addresses is more complex on macOS, just succeed | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async fn add_ipv6_route( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|         cost: Option<i32>, | ||||
|     ) -> Result<(), Error> { | ||||
|         let cmd = if let Some(cost) = cost { | ||||
|             format!( | ||||
|                 "route -n add -inet6 {}/{} -interface {} -hopcount {}", | ||||
|                 address, cidr_prefix, name, cost | ||||
|             ) | ||||
|         } else { | ||||
|             format!( | ||||
|                 "route -n add -inet6 {}/{} -interface {}", | ||||
|                 address, cidr_prefix, name | ||||
|             ) | ||||
|         }; | ||||
|         run_shell_cmd(cmd.as_str()).await | ||||
|     } | ||||
|  | ||||
|     async fn remove_ipv6_route( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         run_shell_cmd( | ||||
|             format!( | ||||
|                 "route -n delete -inet6 {}/{} -interface {}", | ||||
|                 address, cidr_prefix, name | ||||
|             ) | ||||
|             .as_str(), | ||||
|         ) | ||||
|         .await | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -7,7 +7,7 @@ mod windows; | ||||
|  | ||||
| mod route; | ||||
|  | ||||
| use std::net::Ipv4Addr; | ||||
| use std::net::{Ipv4Addr, Ipv6Addr}; | ||||
|  | ||||
| use async_trait::async_trait; | ||||
| use tokio::process::Command; | ||||
| @@ -41,12 +41,40 @@ pub trait IfConfiguerTrait: Send + Sync { | ||||
|     ) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn add_ipv6_route( | ||||
|         &self, | ||||
|         _name: &str, | ||||
|         _address: Ipv6Addr, | ||||
|         _cidr_prefix: u8, | ||||
|         _cost: Option<i32>, | ||||
|     ) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn remove_ipv6_route( | ||||
|         &self, | ||||
|         _name: &str, | ||||
|         _address: Ipv6Addr, | ||||
|         _cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn add_ipv6_ip( | ||||
|         &self, | ||||
|         _name: &str, | ||||
|         _address: Ipv6Addr, | ||||
|         _cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn set_link_status(&self, _name: &str, _up: bool) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn remove_ip(&self, _name: &str, _ip: Option<Ipv4Addr>) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn remove_ipv6(&self, _name: &str, _ip: Option<Ipv6Addr>) -> Result<(), Error> { | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn wait_interface_show(&self, _name: &str) -> Result<(), Error> { | ||||
|         return Ok(()); | ||||
|     } | ||||
|   | ||||
| @@ -194,6 +194,32 @@ impl NetlinkIfConfiger { | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     fn get_prefix_len_ipv6(name: &str, ip: Ipv6Addr) -> Result<u8, Error> { | ||||
|         let addrs = Self::list_addresses(name)?; | ||||
|         for addr in addrs { | ||||
|             if addr.address() == IpAddr::V6(ip) { | ||||
|                 return Ok(addr.network_length()); | ||||
|             } | ||||
|         } | ||||
|         Err(Error::NotFound) | ||||
|     } | ||||
|  | ||||
|     fn remove_one_ipv6(name: &str, ip: Ipv6Addr, prefix_len: u8) -> Result<(), Error> { | ||||
|         let mut message = AddressMessage::default(); | ||||
|         message.header.prefix_len = prefix_len; | ||||
|         message.header.index = NetlinkIfConfiger::get_interface_index(name)?; | ||||
|         message.header.family = AddressFamily::Inet6; | ||||
|  | ||||
|         message | ||||
|             .attributes | ||||
|             .push(AddressAttribute::Address(std::net::IpAddr::V6(ip))); | ||||
|  | ||||
|         send_netlink_req_and_wait_one_resp::<RouteNetlinkMessage>( | ||||
|             RouteNetlinkMessage::DelAddress(message), | ||||
|             true, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn mtu_op<T: TryInto<Ioctl>>( | ||||
|         name: &str, | ||||
|         op: T, | ||||
| @@ -469,6 +495,106 @@ impl IfConfiguerTrait for NetlinkIfConfiger { | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn add_ipv6_ip( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         let mut message = AddressMessage::default(); | ||||
|  | ||||
|         message.header.prefix_len = cidr_prefix; | ||||
|         message.header.index = NetlinkIfConfiger::get_interface_index(name)?; | ||||
|         message.header.family = AddressFamily::Inet6; | ||||
|  | ||||
|         message | ||||
|             .attributes | ||||
|             .push(AddressAttribute::Address(std::net::IpAddr::V6(address))); | ||||
|  | ||||
|         // For IPv6, we don't need IFA_LOCAL or IFA_BROADCAST | ||||
|         send_netlink_req_and_wait_one_resp::<RouteNetlinkMessage>( | ||||
|             RouteNetlinkMessage::NewAddress(message), | ||||
|             false, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     async fn remove_ipv6(&self, name: &str, ip: Option<std::net::Ipv6Addr>) -> Result<(), Error> { | ||||
|         if ip.is_none() { | ||||
|             let addrs = Self::list_addresses(name)?; | ||||
|             for addr in addrs { | ||||
|                 if let IpAddr::V6(ipv6) = addr.address() { | ||||
|                     let prefix_len = addr.network_length(); | ||||
|                     Self::remove_one_ipv6(name, ipv6, prefix_len)?; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             let ipv6 = ip.unwrap(); | ||||
|             let prefix_len = Self::get_prefix_len_ipv6(name, ipv6)?; | ||||
|             Self::remove_one_ipv6(name, ipv6, prefix_len)?; | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     async fn add_ipv6_route( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|         cost: Option<i32>, | ||||
|     ) -> Result<(), Error> { | ||||
|         let mut message = RouteMessage::default(); | ||||
|  | ||||
|         message.header.address_family = AddressFamily::Inet6; | ||||
|         message.header.destination_prefix_length = cidr_prefix; | ||||
|         message.header.table = RouteHeader::RT_TABLE_MAIN; | ||||
|         message.header.protocol = RouteProtocol::Static; | ||||
|         message.header.scope = RouteScope::Universe; | ||||
|         message.header.kind = RouteType::Unicast; | ||||
|  | ||||
|         // Add metric (cost) if specified | ||||
|         if let Some(cost) = cost { | ||||
|             message | ||||
|                 .attributes | ||||
|                 .push(RouteAttribute::Priority(cost as u32)); | ||||
|         } | ||||
|  | ||||
|         message | ||||
|             .attributes | ||||
|             .push(RouteAttribute::Oif(NetlinkIfConfiger::get_interface_index( | ||||
|                 name, | ||||
|             )?)); | ||||
|  | ||||
|         message | ||||
|             .attributes | ||||
|             .push(RouteAttribute::Destination(RouteAddress::Inet6(address))); | ||||
|  | ||||
|         send_netlink_req_and_wait_one_resp(RouteNetlinkMessage::NewRoute(message), false) | ||||
|     } | ||||
|  | ||||
|     async fn remove_ipv6_route( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         let routes = Self::list_routes()?; | ||||
|         let ifidx = NetlinkIfConfiger::get_interface_index(name)?; | ||||
|  | ||||
|         for msg in routes { | ||||
|             let other_route: Route = msg.clone().into(); | ||||
|             if other_route.destination == std::net::IpAddr::V6(address) | ||||
|                 && other_route.prefix == cidr_prefix | ||||
|                 && other_route.ifindex == Some(ifidx) | ||||
|             { | ||||
|                 send_netlink_req_and_wait_one_resp(RouteNetlinkMessage::DelRoute(msg), true)?; | ||||
|                 return Ok(()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
|   | ||||
| @@ -169,6 +169,74 @@ impl IfConfiguerTrait for WindowsIfConfiger { | ||||
|         ) | ||||
|         .await | ||||
|     } | ||||
|  | ||||
|     async fn add_ipv6_ip( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         run_shell_cmd( | ||||
|             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> { | ||||
|         if let Some(ip) = ip { | ||||
|             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( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|         cost: Option<i32>, | ||||
|     ) -> Result<(), Error> { | ||||
|         let cmd = if let Some(cost) = cost { | ||||
|             format!( | ||||
|                 "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 | ||||
|     } | ||||
|  | ||||
|     async fn remove_ipv6_route( | ||||
|         &self, | ||||
|         name: &str, | ||||
|         address: std::net::Ipv6Addr, | ||||
|         cidr_prefix: u8, | ||||
|     ) -> Result<(), Error> { | ||||
|         run_shell_cmd( | ||||
|             format!( | ||||
|                 "netsh interface ipv6 delete route {}/{} {}", | ||||
|                 address, cidr_prefix, name | ||||
|             ) | ||||
|             .as_str(), | ||||
|         ) | ||||
|         .await | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct RegistryManager; | ||||
|   | ||||
| @@ -148,6 +148,13 @@ struct NetworkOptions { | ||||
|     )] | ||||
|     ipv4: Option<String>, | ||||
|  | ||||
|     #[arg( | ||||
|         long, | ||||
|         env = "ET_IPV6", | ||||
|         help = t!("core_clap.ipv6").to_string() | ||||
|     )] | ||||
|     ipv6: Option<String>, | ||||
|  | ||||
|     #[arg( | ||||
|         short, | ||||
|         long, | ||||
| @@ -615,6 +622,12 @@ impl NetworkOptions { | ||||
|             })?)) | ||||
|         } | ||||
|  | ||||
|         if let Some(ipv6) = &self.ipv6 { | ||||
|             cfg.set_ipv6(Some(ipv6.parse().with_context(|| { | ||||
|                 format!("failed to parse ipv6 address: {}", ipv6) | ||||
|             })?)) | ||||
|         } | ||||
|  | ||||
|         if !self.peers.is_empty() { | ||||
|             let mut peers = cfg.get_peers(); | ||||
|             peers.reserve(peers.len() + self.peers.len()); | ||||
|   | ||||
| @@ -301,7 +301,7 @@ impl Socks5ServerNet { | ||||
|  | ||||
|                 let dst = ipv4.get_destination(); | ||||
|                 let packet = ZCPacket::new_with_payload(&data); | ||||
|                 if let Err(e) = peer_manager.send_msg_ipv4(packet, dst).await { | ||||
|                 if let Err(e) = peer_manager.send_msg_by_ip(packet, IpAddr::V4(dst)).await { | ||||
|                     tracing::error!("send to peer failed in smoltcp sender: {:?}", e); | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -557,7 +557,7 @@ impl<C: NatDstConnector> TcpProxy<C> { | ||||
|  | ||||
|                     let dst = ipv4.get_destination(); | ||||
|                     let packet = ZCPacket::new_with_payload(&data); | ||||
|                     if let Err(e) = peer_mgr.send_msg_ipv4(packet, dst).await { | ||||
|                     if let Err(e) = peer_mgr.send_msg_by_ip(packet, IpAddr::V4(dst)).await { | ||||
|                         tracing::error!("send to peer failed in smoltcp sender: {:?}", e); | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -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(()) | ||||
|   | ||||
| @@ -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(()); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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(); | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -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, | ||||
|   | ||||
| @@ -65,6 +65,8 @@ message Route { | ||||
|   optional uint32 next_hop_peer_id_latency_first = 12; | ||||
|   optional int32 cost_latency_first = 13; | ||||
|   optional int32 path_latency_latency_first = 14; | ||||
|  | ||||
|   common.Ipv6Inet ipv6_addr = 15; | ||||
| } | ||||
|  | ||||
| message PeerRoutePair { | ||||
|   | ||||
| @@ -139,6 +139,11 @@ message Ipv4Inet { | ||||
|   uint32 network_length = 2; | ||||
| } | ||||
|  | ||||
| message Ipv6Inet { | ||||
|   Ipv6Addr address = 1; | ||||
|   uint32 network_length = 2; | ||||
| } | ||||
|  | ||||
| message Url { string url = 1; } | ||||
|  | ||||
| message SocketAddr { | ||||
|   | ||||
| @@ -131,6 +131,41 @@ impl FromStr for Ipv4Inet { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<cidr::Ipv6Inet> for Ipv6Inet { | ||||
|     fn from(value: cidr::Ipv6Inet) -> Self { | ||||
|         Ipv6Inet { | ||||
|             address: Some(value.address().into()), | ||||
|             network_length: value.network_length() as u32, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Ipv6Inet> for cidr::Ipv6Inet { | ||||
|     fn from(value: Ipv6Inet) -> Self { | ||||
|         cidr::Ipv6Inet::new( | ||||
|             value.address.unwrap_or_default().into(), | ||||
|             value.network_length as u8, | ||||
|         ) | ||||
|         .unwrap() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl fmt::Display for Ipv6Inet { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         write!(f, "{}", cidr::Ipv6Inet::from(self.clone())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl FromStr for Ipv6Inet { | ||||
|     type Err = anyhow::Error; | ||||
|  | ||||
|     fn from_str(s: &str) -> Result<Self, Self::Err> { | ||||
|         Ok(Ipv6Inet::from( | ||||
|             cidr::Ipv6Inet::from_str(s).with_context(|| "Failed to parse Ipv6Inet")?, | ||||
|         )) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<url::Url> for Url { | ||||
|     fn from(value: url::Url) -> Self { | ||||
|         Url { | ||||
|   | ||||
| @@ -24,6 +24,7 @@ message RoutePeerInfo { | ||||
|   uint32 network_length = 13; | ||||
|  | ||||
|   optional uint32 quic_port = 14; | ||||
|   optional common.Ipv6Inet ipv6_addr = 15; | ||||
| } | ||||
|  | ||||
| message PeerIdVersion { | ||||
|   | ||||
							
								
								
									
										66
									
								
								easytier/src/tests/ipv6_test.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								easytier/src/tests/ipv6_test.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| use std::net::Ipv6Addr; | ||||
|  | ||||
| use crate::{ | ||||
|     common::config::{ConfigLoader, TomlConfigLoader}, | ||||
|     common::global_ctx::tests::get_mock_global_ctx, | ||||
|     peers::peer_manager::RouteAlgoType, | ||||
|     proto::peer_rpc::RoutePeerInfo, | ||||
| }; | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn test_ipv6_config_support() { | ||||
|     let config = TomlConfigLoader::default(); | ||||
|  | ||||
|     // Test IPv6 configuration setting and getting | ||||
|     let ipv6_cidr = "fd00::1/64".parse().unwrap(); | ||||
|     config.set_ipv6(Some(ipv6_cidr)); | ||||
|  | ||||
|     assert_eq!(config.get_ipv6(), Some(ipv6_cidr)); | ||||
| } | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn test_global_ctx_ipv6() { | ||||
|     let global_ctx = get_mock_global_ctx(); | ||||
|  | ||||
|     // Test setting and getting IPv6 from global context | ||||
|     let ipv6_cidr = "fd00::1/64".parse().unwrap(); | ||||
|     global_ctx.set_ipv6(Some(ipv6_cidr)); | ||||
|  | ||||
|     assert_eq!(global_ctx.get_ipv6(), Some(ipv6_cidr)); | ||||
| } | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn test_route_peer_info_ipv6() { | ||||
|     let global_ctx = get_mock_global_ctx(); | ||||
|  | ||||
|     // Set IPv6 address in global context | ||||
|     let ipv6_cidr = "fd00::1/64".parse().unwrap(); | ||||
|     global_ctx.set_ipv6(Some(ipv6_cidr)); | ||||
|  | ||||
|     // Create RoutePeerInfo with IPv6 support | ||||
|     let peer_info = RoutePeerInfo::new(); | ||||
|     let updated_info = peer_info.update_self(123, 456, &global_ctx); | ||||
|  | ||||
|     // Verify IPv6 address is included | ||||
|     assert!(updated_info.ipv6_addr.is_some()); | ||||
|     let ipv6_addr: Ipv6Addr = updated_info.ipv6_addr.unwrap().address.unwrap().into(); | ||||
|     assert_eq!(ipv6_addr, ipv6_cidr.address()); | ||||
| } | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn test_peer_manager_ipv6() { | ||||
|     let global_ctx = get_mock_global_ctx(); | ||||
|     let (packet_sender, _packet_receiver) = tokio::sync::mpsc::channel(100); | ||||
|     let peer_mgr = crate::peers::peer_manager::PeerManager::new( | ||||
|         RouteAlgoType::Ospf, | ||||
|         global_ctx.clone(), | ||||
|         packet_sender, | ||||
|     ); | ||||
|  | ||||
|     // Test IPv6 address lookup for unknown address | ||||
|     let ipv6_addr = Ipv6Addr::new(0xfd00, 0, 0, 0, 0, 0, 0, 2); | ||||
|     let (peers, _is_self) = peer_mgr.get_msg_dst_peer_ipv6(&ipv6_addr).await; | ||||
|  | ||||
|     // Should return empty peers list for unknown IPv6 | ||||
|     assert!(peers.is_empty()); | ||||
| } | ||||
| @@ -1,6 +1,8 @@ | ||||
| #[cfg(target_os = "linux")] | ||||
| mod three_node; | ||||
|  | ||||
| mod ipv6_test; | ||||
|  | ||||
| use crate::common::PeerId; | ||||
| use crate::peers::peer_manager::PeerManager; | ||||
|  | ||||
|   | ||||
| @@ -51,11 +51,17 @@ pub fn prepare_linux_namespaces() { | ||||
|     add_ns_to_bridge("br_b", "net_d"); | ||||
| } | ||||
|  | ||||
| pub fn get_inst_config(inst_name: &str, ns: Option<&str>, ipv4: &str) -> TomlConfigLoader { | ||||
| pub fn get_inst_config( | ||||
|     inst_name: &str, | ||||
|     ns: Option<&str>, | ||||
|     ipv4: &str, | ||||
|     ipv6: &str, | ||||
| ) -> TomlConfigLoader { | ||||
|     let config = TomlConfigLoader::default(); | ||||
|     config.set_inst_name(inst_name.to_owned()); | ||||
|     config.set_netns(ns.map(|s| s.to_owned())); | ||||
|     config.set_ipv4(Some(ipv4.parse().unwrap())); | ||||
|     config.set_ipv6(Some(ipv6.parse().unwrap())); | ||||
|     config.set_listeners(vec![ | ||||
|         "tcp://0.0.0.0:11010".parse().unwrap(), | ||||
|         "udp://0.0.0.0:11010".parse().unwrap(), | ||||
| @@ -82,16 +88,19 @@ pub async fn init_three_node_ex<F: Fn(TomlConfigLoader) -> TomlConfigLoader>( | ||||
|         "inst1", | ||||
|         Some("net_a"), | ||||
|         "10.144.144.1", | ||||
|         "fd00::1/64", | ||||
|     ))); | ||||
|     let mut inst2 = Instance::new(cfg_cb(get_inst_config( | ||||
|         "inst2", | ||||
|         Some("net_b"), | ||||
|         "10.144.144.2", | ||||
|         "fd00::2/64", | ||||
|     ))); | ||||
|     let mut inst3 = Instance::new(cfg_cb(get_inst_config( | ||||
|         "inst3", | ||||
|         Some("net_c"), | ||||
|         "10.144.144.3", | ||||
|         "fd00::3/64", | ||||
|     ))); | ||||
|  | ||||
|     inst1.run().await.unwrap(); | ||||
| @@ -232,6 +241,30 @@ async fn ping_test(from_netns: &str, target_ip: &str, payload_size: Option<usize | ||||
|     code.code().unwrap() == 0 | ||||
| } | ||||
|  | ||||
| async fn ping6_test(from_netns: &str, target_ip: &str, payload_size: Option<usize>) -> bool { | ||||
|     let _g = NetNS::new(Some(ROOT_NETNS_NAME.to_owned())).guard(); | ||||
|     let code = tokio::process::Command::new("ip") | ||||
|         .args(&[ | ||||
|             "netns", | ||||
|             "exec", | ||||
|             from_netns, | ||||
|             "ping6", | ||||
|             "-c", | ||||
|             "1", | ||||
|             "-s", | ||||
|             payload_size.unwrap_or(56).to_string().as_str(), | ||||
|             "-W", | ||||
|             "1", | ||||
|             target_ip.to_string().as_str(), | ||||
|         ]) | ||||
|         .stdout(std::process::Stdio::null()) | ||||
|         .stderr(std::process::Stdio::null()) | ||||
|         .status() | ||||
|         .await | ||||
|         .unwrap(); | ||||
|     code.code().unwrap() == 0 | ||||
| } | ||||
|  | ||||
| #[rstest::rstest] | ||||
| #[tokio::test] | ||||
| #[serial_test::serial] | ||||
| @@ -250,12 +283,26 @@ pub async fn basic_three_node_test(#[values("tcp", "udp", "wg", "ws", "wss")] pr | ||||
|         insts[0].get_peer_manager().list_routes().await, | ||||
|     ); | ||||
|  | ||||
|     // Test IPv4 connectivity | ||||
|     wait_for_condition( | ||||
|         || async { ping_test("net_c", "10.144.144.1", None).await }, | ||||
|         Duration::from_secs(5000), | ||||
|     ) | ||||
|     .await; | ||||
|  | ||||
|     // Test IPv6 connectivity | ||||
|     wait_for_condition( | ||||
|         || async { ping6_test("net_c", "fd00::1", None).await }, | ||||
|         Duration::from_secs(5), | ||||
|     ) | ||||
|     .await; | ||||
|  | ||||
|     wait_for_condition( | ||||
|         || async { ping6_test("net_a", "fd00::3", None).await }, | ||||
|         Duration::from_secs(5), | ||||
|     ) | ||||
|     .await; | ||||
|  | ||||
|     drop_insts(insts).await; | ||||
| } | ||||
|  | ||||
| @@ -562,7 +609,12 @@ pub async fn proxy_three_node_disconnect_test(#[values("tcp", "wg")] proto: &str | ||||
|     }; | ||||
|  | ||||
|     let insts = init_three_node(proto).await; | ||||
|     let mut inst4 = Instance::new(get_inst_config("inst4", Some("net_d"), "10.144.144.4")); | ||||
|     let mut inst4 = Instance::new(get_inst_config( | ||||
|         "inst4", | ||||
|         Some("net_d"), | ||||
|         "10.144.144.4", | ||||
|         "fd00::4/64", | ||||
|     )); | ||||
|     if proto == "tcp" { | ||||
|         inst4 | ||||
|             .get_conn_manager() | ||||
| @@ -627,16 +679,7 @@ pub async fn proxy_three_node_disconnect_test(#[values("tcp", "wg")] proto: &str | ||||
|                         .iter() | ||||
|                         .find(|r| **r == inst4.peer_id()) | ||||
|                         .is_none(); | ||||
|                     if !ret { | ||||
|                         println!( | ||||
|                             "conn info: {:?}", | ||||
|                             insts[2] | ||||
|                                 .get_peer_manager() | ||||
|                                 .get_peer_map() | ||||
|                                 .list_peer_conns(inst4.peer_id()) | ||||
|                                 .await | ||||
|                         ); | ||||
|                     } | ||||
|  | ||||
|                     ret | ||||
|                 }, | ||||
|                 // 0 down, assume last packet is recv in -0.01 | ||||
| @@ -726,13 +769,23 @@ pub async fn udp_broadcast_test() { | ||||
| pub async fn foreign_network_forward_nic_data() { | ||||
|     prepare_linux_namespaces(); | ||||
|  | ||||
|     let center_node_config = get_inst_config("inst1", Some("net_a"), "10.144.144.1"); | ||||
|     let center_node_config = get_inst_config("inst1", Some("net_a"), "10.144.144.1", "fd00::1/64"); | ||||
|     center_node_config | ||||
|         .set_network_identity(NetworkIdentity::new("center".to_string(), "".to_string())); | ||||
|     let mut center_inst = Instance::new(center_node_config); | ||||
|  | ||||
|     let mut inst1 = Instance::new(get_inst_config("inst1", Some("net_b"), "10.144.145.1")); | ||||
|     let mut inst2 = Instance::new(get_inst_config("inst2", Some("net_c"), "10.144.145.2")); | ||||
|     let mut inst1 = Instance::new(get_inst_config( | ||||
|         "inst1", | ||||
|         Some("net_b"), | ||||
|         "10.144.145.1", | ||||
|         "fd00:1::1/64", | ||||
|     )); | ||||
|     let mut inst2 = Instance::new(get_inst_config( | ||||
|         "inst2", | ||||
|         Some("net_c"), | ||||
|         "10.144.145.2", | ||||
|         "fd00:1::2/64", | ||||
|     )); | ||||
|  | ||||
|     center_inst.run().await.unwrap(); | ||||
|     inst1.run().await.unwrap(); | ||||
| @@ -940,21 +993,26 @@ pub async fn foreign_network_functional_cluster() { | ||||
|     crate::set_global_var!(OSPF_UPDATE_MY_GLOBAL_FOREIGN_NETWORK_INTERVAL_SEC, 1); | ||||
|     prepare_linux_namespaces(); | ||||
|  | ||||
|     let center_node_config1 = get_inst_config("inst1", Some("net_a"), "10.144.144.1"); | ||||
|     let center_node_config1 = get_inst_config("inst1", Some("net_a"), "10.144.144.1", "fd00::1/64"); | ||||
|     center_node_config1 | ||||
|         .set_network_identity(NetworkIdentity::new("center".to_string(), "".to_string())); | ||||
|     let mut center_inst1 = Instance::new(center_node_config1); | ||||
|  | ||||
|     let center_node_config2 = get_inst_config("inst2", Some("net_b"), "10.144.144.2"); | ||||
|     let center_node_config2 = get_inst_config("inst2", Some("net_b"), "10.144.144.2", "fd00::2/64"); | ||||
|     center_node_config2 | ||||
|         .set_network_identity(NetworkIdentity::new("center".to_string(), "".to_string())); | ||||
|     let mut center_inst2 = Instance::new(center_node_config2); | ||||
|  | ||||
|     let inst1_config = get_inst_config("inst1", Some("net_c"), "10.144.145.1"); | ||||
|     let inst1_config = get_inst_config("inst1", Some("net_c"), "10.144.145.1", "fd00:2::1/64"); | ||||
|     inst1_config.set_listeners(vec![]); | ||||
|     let mut inst1 = Instance::new(inst1_config); | ||||
|  | ||||
|     let mut inst2 = Instance::new(get_inst_config("inst2", Some("net_d"), "10.144.145.2")); | ||||
|     let mut inst2 = Instance::new(get_inst_config( | ||||
|         "inst2", | ||||
|         Some("net_d"), | ||||
|         "10.144.145.2", | ||||
|         "fd00:2::2/64", | ||||
|     )); | ||||
|  | ||||
|     center_inst1.run().await.unwrap(); | ||||
|     center_inst2.run().await.unwrap(); | ||||
| @@ -1011,18 +1069,23 @@ pub async fn foreign_network_functional_cluster() { | ||||
| pub async fn manual_reconnector(#[values(true, false)] is_foreign: bool) { | ||||
|     prepare_linux_namespaces(); | ||||
|  | ||||
|     let center_node_config = get_inst_config("inst1", Some("net_a"), "10.144.144.1"); | ||||
|     let center_node_config = get_inst_config("inst1", Some("net_a"), "10.144.144.1", "fd00::1/64"); | ||||
|     if is_foreign { | ||||
|         center_node_config | ||||
|             .set_network_identity(NetworkIdentity::new("center".to_string(), "".to_string())); | ||||
|     } | ||||
|     let mut center_inst = Instance::new(center_node_config); | ||||
|  | ||||
|     let inst1_config = get_inst_config("inst1", Some("net_b"), "10.144.145.1"); | ||||
|     let inst1_config = get_inst_config("inst1", Some("net_b"), "10.144.145.1", "fd00:1::1/64"); | ||||
|     inst1_config.set_listeners(vec![]); | ||||
|     let mut inst1 = Instance::new(inst1_config); | ||||
|  | ||||
|     let mut inst2 = Instance::new(get_inst_config("inst2", Some("net_c"), "10.144.145.2")); | ||||
|     let mut inst2 = Instance::new(get_inst_config( | ||||
|         "inst2", | ||||
|         Some("net_c"), | ||||
|         "10.144.145.2", | ||||
|         "fd00:1::2/64", | ||||
|     )); | ||||
|  | ||||
|     center_inst.run().await.unwrap(); | ||||
|     inst1.run().await.unwrap(); | ||||
|   | ||||
| @@ -388,7 +388,12 @@ pub(crate) fn setup_sokcet2_ext( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux", target_env = "ohos"))] | ||||
|     #[cfg(any( | ||||
|         target_os = "android", | ||||
|         target_os = "fuchsia", | ||||
|         target_os = "linux", | ||||
|         target_env = "ohos" | ||||
|     ))] | ||||
|     if let Some(dev_name) = bind_dev { | ||||
|         tracing::trace!(dev_name = ?dev_name, "bind device"); | ||||
|         socket2_socket.bind_device(Some(dev_name.as_bytes()))?; | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use std::{ | ||||
|     net::{Ipv4Addr, SocketAddr}, | ||||
|     net::{IpAddr, Ipv4Addr, SocketAddr}, | ||||
|     sync::Arc, | ||||
| }; | ||||
|  | ||||
| @@ -128,7 +128,7 @@ impl WireGuardImpl { | ||||
|             tracing::trace!(?i, "Received from wg client"); | ||||
|             let dst = i.get_destination(); | ||||
|             let _ = peer_mgr | ||||
|                 .send_msg_ipv4(ZCPacket::new_with_payload(inner.as_ref()), dst) | ||||
|                 .send_msg_by_ip(ZCPacket::new_with_payload(inner.as_ref()), IpAddr::V4(dst)) | ||||
|                 .await; | ||||
|         } | ||||
|  | ||||
|   | ||||
							
								
								
									
										82
									
								
								flake.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								flake.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | ||||
| { | ||||
|   "nodes": { | ||||
|     "flake-utils": { | ||||
|       "inputs": { | ||||
|         "systems": "systems" | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1731533236, | ||||
|         "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", | ||||
|         "owner": "numtide", | ||||
|         "repo": "flake-utils", | ||||
|         "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "numtide", | ||||
|         "repo": "flake-utils", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "nixpkgs": { | ||||
|       "locked": { | ||||
|         "lastModified": 1750741721, | ||||
|         "narHash": "sha256-Z0djmTa1YmnGMfE9jEe05oO4zggjDmxOGKwt844bUhE=", | ||||
|         "owner": "NixOS", | ||||
|         "repo": "nixpkgs", | ||||
|         "rev": "4b1164c3215f018c4442463a27689d973cffd750", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "NixOS", | ||||
|         "ref": "nixos-unstable", | ||||
|         "repo": "nixpkgs", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "root": { | ||||
|       "inputs": { | ||||
|         "flake-utils": "flake-utils", | ||||
|         "nixpkgs": "nixpkgs", | ||||
|         "rust-overlay": "rust-overlay" | ||||
|       } | ||||
|     }, | ||||
|     "rust-overlay": { | ||||
|       "inputs": { | ||||
|         "nixpkgs": [ | ||||
|           "nixpkgs" | ||||
|         ] | ||||
|       }, | ||||
|       "locked": { | ||||
|         "lastModified": 1750905536, | ||||
|         "narHash": "sha256-Mo7yXM5IvMGNvJPiNkFsVT2UERmnvjsKgnY6UyDdySQ=", | ||||
|         "owner": "oxalica", | ||||
|         "repo": "rust-overlay", | ||||
|         "rev": "2fa7c0aabd15fa0ccc1dc7e675a4fcf0272ad9a1", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "oxalica", | ||||
|         "repo": "rust-overlay", | ||||
|         "type": "github" | ||||
|       } | ||||
|     }, | ||||
|     "systems": { | ||||
|       "locked": { | ||||
|         "lastModified": 1681028828, | ||||
|         "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", | ||||
|         "owner": "nix-systems", | ||||
|         "repo": "default", | ||||
|         "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", | ||||
|         "type": "github" | ||||
|       }, | ||||
|       "original": { | ||||
|         "owner": "nix-systems", | ||||
|         "repo": "default", | ||||
|         "type": "github" | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   "root": "root", | ||||
|   "version": 7 | ||||
| } | ||||
							
								
								
									
										44
									
								
								flake.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								flake.nix
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| { | ||||
|   description = "EasyTier development environment"; | ||||
|  | ||||
|   inputs = { | ||||
|     nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | ||||
|     flake-utils.url = "github:numtide/flake-utils"; | ||||
|     rust-overlay = { | ||||
|       url = "github:oxalica/rust-overlay"; | ||||
|       inputs.nixpkgs.follows = "nixpkgs"; | ||||
|     }; | ||||
|   }; | ||||
|  | ||||
|   outputs = { self, nixpkgs, flake-utils, rust-overlay }: | ||||
|     flake-utils.lib.eachDefaultSystem (system: | ||||
|       let | ||||
|         overlays = [ (import rust-overlay) ]; | ||||
|         pkgs = import nixpkgs { | ||||
|           inherit system overlays; | ||||
|         }; | ||||
|  | ||||
|         lib = nixpkgs.lib; | ||||
|  | ||||
|         rust = pkgs.rust-bin.stable.latest.default.override { | ||||
|           extensions = [ "rust-src" "rust-analyzer" ]; | ||||
|         }; | ||||
|       in | ||||
|       { | ||||
|         devShells.default = pkgs.mkShell { | ||||
|           nativeBuildInputs = with pkgs; [ | ||||
|             rust | ||||
|             pkg-config | ||||
|             protobuf | ||||
|           ]; | ||||
|  | ||||
|           buildInputs = with pkgs; [ | ||||
|             zstd | ||||
|           ]; | ||||
|  | ||||
|           LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; | ||||
|           ZSTD_SYS_USE_PKG_CONFIG = true; | ||||
|           KCP_SYS_EXTRA_HEADER_PATH = "${pkgs.libclang.lib}/lib/clang/19/include:${pkgs.glibc.dev}/include"; | ||||
|         }; | ||||
|       }); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 DavHau
					DavHau