mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-20 11:22:39 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
003fdefc63 | ||
|
|
a25c411249 | ||
|
|
0c11cefc04 | ||
|
|
8181830902 | ||
|
|
0a8c95879b |
@@ -49,8 +49,8 @@ core_clap:
|
||||
en: "manually specify the public IPv6 subnet to share, instead of auto-detecting from system routes"
|
||||
zh-CN: "手动指定要共享的公网 IPv6 子网,不自动从系统路由检测"
|
||||
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将自动更改。"
|
||||
en: "automatically determine and set IP address by Easytier. The subnet is derived from a connected peer's IPv4 or defaults to 10.126.126.0/24. Warning, if there is an IP conflict in the network when using DHCP, the IP will be automatically changed. Optionally specify a CIDR subnet (e.g. -d 10.0.0.0/24, prefix <= /30) to pin the DHCP address range."
|
||||
zh-CN: "由Easytier自动确定并设置IP地址。子网从已连接对等节点的IPv4派生,或默认使用10.126.126.0/24。警告:在使用DHCP时,如果网络中出现IP冲突,IP将自动更改。可选指定CIDR子网(如 -d 10.0.0.0/24,前缀 <= /30)来固定DHCP地址范围。"
|
||||
peers:
|
||||
en: "peers to connect initially"
|
||||
zh-CN: "最初要连接的对等节点"
|
||||
|
||||
@@ -185,6 +185,9 @@ pub trait ConfigLoader: Send + Sync {
|
||||
fn get_dhcp(&self) -> bool;
|
||||
fn set_dhcp(&self, dhcp: bool);
|
||||
|
||||
fn get_dhcp_cidr(&self) -> Option<cidr::Ipv4Cidr>;
|
||||
fn set_dhcp_cidr(&self, cidr: Option<cidr::Ipv4Cidr>);
|
||||
|
||||
fn add_proxy_cidr(
|
||||
&self,
|
||||
cidr: cidr::Ipv4Cidr,
|
||||
@@ -535,6 +538,7 @@ struct Config {
|
||||
ipv6_public_addr_auto: Option<bool>,
|
||||
ipv6_public_addr_prefix: Option<String>,
|
||||
dhcp: Option<bool>,
|
||||
dhcp_cidr: Option<String>,
|
||||
network_identity: Option<NetworkIdentity>,
|
||||
listeners: Option<Vec<url::Url>>,
|
||||
mapped_listeners: Option<Vec<url::Url>>,
|
||||
@@ -761,13 +765,26 @@ impl ConfigLoader for TomlConfigLoader {
|
||||
}
|
||||
|
||||
fn get_dhcp(&self) -> bool {
|
||||
self.config.lock().unwrap().dhcp.unwrap_or_default()
|
||||
let config = self.config.lock().unwrap();
|
||||
config.dhcp.unwrap_or_default() || config.dhcp_cidr.is_some()
|
||||
}
|
||||
|
||||
fn set_dhcp(&self, dhcp: bool) {
|
||||
self.config.lock().unwrap().dhcp = Some(dhcp);
|
||||
}
|
||||
|
||||
fn get_dhcp_cidr(&self) -> Option<cidr::Ipv4Cidr> {
|
||||
let locked_config = self.config.lock().unwrap();
|
||||
locked_config
|
||||
.dhcp_cidr
|
||||
.as_ref()
|
||||
.and_then(|s| s.parse().ok())
|
||||
}
|
||||
|
||||
fn set_dhcp_cidr(&self, cidr: Option<cidr::Ipv4Cidr>) {
|
||||
self.config.lock().unwrap().dhcp_cidr = cidr.map(|c| c.to_string());
|
||||
}
|
||||
|
||||
fn add_proxy_cidr(
|
||||
&self,
|
||||
cidr: cidr::Ipv4Cidr,
|
||||
|
||||
+20
-3
@@ -204,7 +204,7 @@ struct NetworkOptions {
|
||||
num_args = 0..=1,
|
||||
default_missing_value = "true"
|
||||
)]
|
||||
dhcp: Option<bool>,
|
||||
dhcp: Option<String>,
|
||||
|
||||
#[arg(
|
||||
short,
|
||||
@@ -907,8 +907,25 @@ impl NetworkOptions {
|
||||
cfg.set_network_identity(NetworkIdentity::new_credential(network_name));
|
||||
}
|
||||
|
||||
if let Some(dhcp) = self.dhcp {
|
||||
cfg.set_dhcp(dhcp);
|
||||
if let Some(ref dhcp) = self.dhcp {
|
||||
if dhcp == "true" || dhcp == "1" {
|
||||
cfg.set_dhcp(true);
|
||||
} else if dhcp == "false" || dhcp == "0" {
|
||||
cfg.set_dhcp(false);
|
||||
} else {
|
||||
// Treat as CIDR, e.g. "10.0.0.0/24"
|
||||
cfg.set_dhcp(true);
|
||||
let cidr: cidr::Ipv4Cidr = dhcp
|
||||
.parse()
|
||||
.with_context(|| format!("failed to parse dhcp cidr: {}", dhcp))?;
|
||||
if cidr.network_length() > 30 {
|
||||
anyhow::bail!(
|
||||
"dhcp cidr prefix length must be <= 30, got /{}",
|
||||
cidr.network_length()
|
||||
);
|
||||
}
|
||||
cfg.set_dhcp_cidr(Some(cidr));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ipv4) = &self.ipv4 {
|
||||
|
||||
@@ -829,7 +829,11 @@ impl Instance {
|
||||
let nic_ctx = self.nic_ctx.clone();
|
||||
let _peer_packet_receiver = self.peer_packet_receiver.clone();
|
||||
tokio::spawn(async move {
|
||||
let default_ipv4_addr = Ipv4Inet::new(Ipv4Addr::new(10, 126, 126, 0), 24).unwrap();
|
||||
let default_ipv4_addr = if let Some(dhcp_cidr) = global_ctx_c.config.get_dhcp_cidr() {
|
||||
Ipv4Inet::new(dhcp_cidr.first_address(), dhcp_cidr.network_length()).unwrap()
|
||||
} else {
|
||||
Ipv4Inet::new(Ipv4Addr::new(10, 126, 126, 0), 24).unwrap()
|
||||
};
|
||||
let mut current_dhcp_ip: Option<Ipv4Inet> = None;
|
||||
let mut next_sleep_time = 0;
|
||||
let nic_closed_notifier = Arc::new(Notify::new());
|
||||
@@ -864,7 +868,11 @@ impl Instance {
|
||||
used_ipv4.insert(peer_ipv4_addr.into());
|
||||
}
|
||||
|
||||
let dhcp_inet = used_ipv4.iter().next().unwrap_or(&default_ipv4_addr);
|
||||
let dhcp_inet = if global_ctx_c.config.get_dhcp_cidr().is_some() {
|
||||
&default_ipv4_addr
|
||||
} else {
|
||||
used_ipv4.iter().next().unwrap_or(&default_ipv4_addr)
|
||||
};
|
||||
// if old ip is already in this subnet and not conflicted, use it
|
||||
if let Some(ip) = current_dhcp_ip
|
||||
&& ip.network() == dhcp_inet.network()
|
||||
@@ -1810,4 +1818,83 @@ mod tests {
|
||||
|
||||
assert!(InstanceConfigPatcher::validate_public_ipv6_patch(&global_ctx, &patch).is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_dhcp_cidr_allocates_ip_in_specified_subnet() {
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::common::config::{ConfigLoader, TomlConfigLoader};
|
||||
use crate::instance::instance::Instance;
|
||||
use crate::tunnel::common::tests::wait_for_condition;
|
||||
use crate::tunnel::ring::RingTunnelConnector;
|
||||
|
||||
// inst1: static IP, no DHCP (acts as a peer so DHCP on inst2 can proceed)
|
||||
let config1 = TomlConfigLoader::default();
|
||||
config1.set_inst_name("dhcp_test_inst1".to_owned());
|
||||
config1.set_ipv4(Some("192.168.200.1/24".parse().unwrap()));
|
||||
let mut flags1 = config1.get_flags();
|
||||
flags1.no_tun = true;
|
||||
config1.set_flags(flags1);
|
||||
config1.set_listeners(vec![]);
|
||||
|
||||
// inst2: DHCP enabled with specific CIDR
|
||||
let config2 = TomlConfigLoader::default();
|
||||
config2.set_inst_name("dhcp_test_inst2".to_owned());
|
||||
config2.set_dhcp(true);
|
||||
config2.set_dhcp_cidr(Some("172.20.0.0/24".parse().unwrap()));
|
||||
let mut flags2 = config2.get_flags();
|
||||
flags2.no_tun = true;
|
||||
config2.set_flags(flags2);
|
||||
config2.set_listeners(vec![]);
|
||||
|
||||
let mut inst1 = Instance::new(config1);
|
||||
let mut inst2 = Instance::new(config2);
|
||||
|
||||
inst1.run().await.unwrap();
|
||||
inst2.run().await.unwrap();
|
||||
|
||||
// Connect inst2 to inst1 via ring tunnel
|
||||
inst2
|
||||
.get_conn_manager()
|
||||
.add_connector(RingTunnelConnector::new(
|
||||
format!("ring://{}", inst1.id()).parse().unwrap(),
|
||||
));
|
||||
|
||||
// Wait for inst2 to see inst1 in routes
|
||||
let pm2 = inst2.get_peer_manager();
|
||||
wait_for_condition(
|
||||
|| async {
|
||||
let routes = pm2.list_routes().await;
|
||||
!routes.is_empty()
|
||||
},
|
||||
Duration::from_secs(5),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Wait for DHCP to allocate an IP on inst2
|
||||
let global_ctx2 = inst2.get_global_ctx();
|
||||
wait_for_condition(
|
||||
|| async { global_ctx2.get_ipv4().is_some() },
|
||||
Duration::from_secs(15),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Verify allocated IP is within the specified CIDR 172.20.0.0/24
|
||||
let allocated_ip = global_ctx2.get_ipv4().unwrap();
|
||||
let expected_cidr: cidr::Ipv4Cidr = "172.20.0.0/24".parse().unwrap();
|
||||
assert!(
|
||||
expected_cidr.contains(&allocated_ip.address()),
|
||||
"Allocated IP {:?} is not in expected CIDR {:?}",
|
||||
allocated_ip,
|
||||
expected_cidr
|
||||
);
|
||||
// Verify the network prefix length matches
|
||||
assert_eq!(
|
||||
allocated_ip.network_length(),
|
||||
expected_cidr.network_length(),
|
||||
"Allocated IP network length {} does not match expected {}",
|
||||
allocated_ip.network_length(),
|
||||
expected_cidr.network_length()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,6 +637,19 @@ impl NetworkConfig {
|
||||
);
|
||||
cfg.set_hostname(self.hostname.clone());
|
||||
cfg.set_dhcp(self.dhcp.unwrap_or_default());
|
||||
if let Some(ref dhcp_cidr) = self.dhcp_cidr {
|
||||
let cidr = dhcp_cidr
|
||||
.parse::<cidr::Ipv4Cidr>()
|
||||
.with_context(|| format!("failed to parse dhcp_cidr: {}", dhcp_cidr))?;
|
||||
if cidr.network_length() > 30 {
|
||||
anyhow::bail!(
|
||||
"dhcp_cidr prefix length must be <= 30, got /{}",
|
||||
cidr.network_length()
|
||||
);
|
||||
}
|
||||
cfg.set_dhcp(true);
|
||||
cfg.set_dhcp_cidr(Some(cidr));
|
||||
}
|
||||
cfg.set_inst_name(self.network_name.clone().unwrap_or_default());
|
||||
|
||||
// The web UI does not expose credential inputs directly, but imported/saved
|
||||
@@ -1019,6 +1032,7 @@ impl NetworkConfig {
|
||||
}
|
||||
|
||||
result.dhcp = Some(config.get_dhcp());
|
||||
result.dhcp_cidr = config.get_dhcp_cidr().map(|c| c.to_string());
|
||||
|
||||
let network_identity = config.get_network_identity();
|
||||
result.network_name = Some(network_identity.network_name.clone());
|
||||
|
||||
@@ -102,6 +102,7 @@ message NetworkConfig {
|
||||
optional bool disable_relay_data = 65;
|
||||
optional bool enable_udp_broadcast_relay = 66;
|
||||
optional uint32 socket_mark = 67;
|
||||
optional string dhcp_cidr = 68;
|
||||
}
|
||||
|
||||
message PortForwardConfig {
|
||||
|
||||
Reference in New Issue
Block a user