diff --git a/easytier/locales/app.yml b/easytier/locales/app.yml index 020bb5e..baa3a2e 100644 --- a/easytier/locales/app.yml +++ b/easytier/locales/app.yml @@ -97,5 +97,17 @@ core_clap: en: "assign routes cidr manually, will disable subnet proxy and wireguard routes propagated from peers. e.g.: 192.168.0.0/16" zh-CN: "手动分配路由CIDR,将禁用子网代理和从对等节点传播的wireguard路由。例如:192.168.0.0/16" relay_network_whitelist: - en: "only forward traffic from the whitelist networks, supporting wildcard strings, prefixes, and multiple network names can be separated by spaces. if this parameter is empty, forwarding is disabled. by default, all networks are allowed. e.g.: '*' (all networks), 'def*' (networks with the prefix 'def'), 'net1 net2' (only allow net1 and net2)" - zh-CN: "仅转发白名单网络的流量,支持通配符字符串、前缀、多个网络名称间可以使用英文空格间隔。如果该参数为空,则禁用转发。默认允许所有网络。例如:'*'(所有网络),'def*'(以def为前缀的网络),'net1 net2'(只允许net1和net2)" \ No newline at end of file + en: |+ + only forward traffic from the whitelist networks, supporting wildcard strings, multiple network names can be separated by spaces. + if this parameter is empty, forwarding is disabled. by default, all networks are allowed. + e.g.: '*' (all networks), 'def*' (networks with the prefix 'def'), 'net1 net2' (only allow net1 and net2)" + zh-CN: |+ + 仅转发白名单网络的流量,支持通配符字符串。多个网络名称间可以使用英文空格间隔。 + 如果该参数为空,则禁用转发。默认允许所有网络。 + 例如:'*'(所有网络),'def*'(以def为前缀的网络),'net1 net2'(只允许net1和net2)" + disable_p2p: + en: "disable p2p communication, will only relay packets with peers specified by --peers" + zh-CN: "禁用P2P通信,只通过--peers指定的节点转发数据包" + relay_all_peer_rpc: + en: "relay all peer rpc packets, even if the peer is not in the relay network whitelist. this can help peers not in relay network whitelist to establish p2p connection." + zh-CN: "转发所有对等节点的RPC数据包,即使对等节点不在转发网络白名单中。这可以帮助白名单外网络中的对等节点建立P2P连接。" \ No newline at end of file diff --git a/easytier/src/common/config.rs b/easytier/src/common/config.rs index 449ecc6..66e5a42 100644 --- a/easytier/src/common/config.rs +++ b/easytier/src/common/config.rs @@ -171,6 +171,10 @@ pub struct Flags { pub use_smoltcp: bool, #[derivative(Default(value = "\"*\".to_string()"))] pub foreign_network_whitelist: String, + #[derivative(Default(value = "false"))] + pub disable_p2p: bool, + #[derivative(Default(value = "false"))] + pub relay_all_peer_rpc: bool, } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)] diff --git a/easytier/src/connector/direct.rs b/easytier/src/connector/direct.rs index af2b8d8..68d5614 100644 --- a/easytier/src/connector/direct.rs +++ b/easytier/src/connector/direct.rs @@ -121,6 +121,10 @@ impl DirectConnectorManager { } pub fn run(&mut self) { + if self.global_ctx.get_flags().disable_p2p { + return; + } + self.run_as_server(); self.run_as_client(); } diff --git a/easytier/src/connector/udp_hole_punch.rs b/easytier/src/connector/udp_hole_punch.rs index fa3c7a7..3970b4b 100644 --- a/easytier/src/connector/udp_hole_punch.rs +++ b/easytier/src/connector/udp_hole_punch.rs @@ -602,6 +602,10 @@ impl UdpHolePunchConnector { } pub async fn run(&mut self) -> Result<(), Error> { + if self.data.global_ctx.get_flags().disable_p2p { + return Ok(()); + } + self.run_as_client().await?; self.run_as_server().await?; diff --git a/easytier/src/easytier-core.rs b/easytier/src/easytier-core.rs index 55f6e5b..5b8c0a5 100644 --- a/easytier/src/easytier-core.rs +++ b/easytier/src/easytier-core.rs @@ -259,6 +259,20 @@ struct Cli { num_args = 0.. )] relay_network_whitelist: Option>, + + #[arg( + long, + help = t!("core_clap.disable_p2p").to_string(), + default_value = "false" + )] + disable_p2p: bool, + + #[arg( + long, + help = t!("core_clap.relay_all_peer_rpc").to_string(), + default_value = "false" + )] + relay_all_peer_rpc: bool, } rust_i18n::i18n!("locales"); @@ -494,6 +508,8 @@ impl From for TomlConfigLoader { if let Some(wl) = cli.relay_network_whitelist { f.foreign_network_whitelist = wl.join(" "); } + f.disable_p2p = cli.disable_p2p; + f.relay_all_peer_rpc = cli.relay_all_peer_rpc; cfg.set_flags(f); cfg.set_exit_nodes(cli.exit_nodes.clone()); diff --git a/easytier/src/peers/foreign_network_manager.rs b/easytier/src/peers/foreign_network_manager.rs index 099feb2..f0fe7f6 100644 --- a/easytier/src/peers/foreign_network_manager.rs +++ b/easytier/src/peers/foreign_network_manager.rs @@ -37,6 +37,7 @@ use super::{ struct ForeignNetworkEntry { network: NetworkIdentity, peer_map: Arc, + relay_data: bool, } impl ForeignNetworkEntry { @@ -45,9 +46,14 @@ impl ForeignNetworkEntry { packet_sender: PacketRecvChan, global_ctx: ArcGlobalCtx, my_peer_id: PeerId, + relay_data: bool, ) -> Self { let peer_map = Arc::new(PeerMap::new(packet_sender, global_ctx, my_peer_id)); - Self { network, peer_map } + Self { + network, + peer_map, + relay_data, + } } } @@ -195,9 +201,30 @@ impl ForeignNetworkManager { } } + fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), Error> { + if self + .global_ctx + .get_flags() + .foreign_network_whitelist + .split(" ") + .map(wildmatch::WildMatch::new) + .any(|wl| wl.matches(network_name)) + { + Ok(()) + } else { + Err(anyhow::anyhow!("network {} not in whitelist", network_name).into()) + } + } + pub async fn add_peer_conn(&self, peer_conn: PeerConn) -> Result<(), Error> { tracing::info!(peer_conn = ?peer_conn.get_conn_info(), network = ?peer_conn.get_network_identity(), "add new peer conn in foreign network manager"); + let relay_peer_rpc = self.global_ctx.get_flags().relay_all_peer_rpc; + let ret = self.check_network_in_whitelist(&peer_conn.get_network_identity().network_name); + if ret.is_err() && !relay_peer_rpc { + return ret; + } + let entry = self .data .network_peer_maps @@ -208,6 +235,7 @@ impl ForeignNetworkManager { self.packet_sender.clone(), self.global_ctx.clone(), self.my_peer_id, + !ret.is_err(), )) }) .clone(); @@ -280,6 +308,10 @@ impl ForeignNetworkManager { } if let Some(entry) = data.get_network_entry(&from_network) { + if !entry.relay_data && hdr.packet_type == PacketType::Data as u8 { + continue; + } + let ret = entry .peer_map .send_msg(packet_bytes, to_peer_id, NextHopPolicy::LeastHop) @@ -424,6 +456,31 @@ mod tests { foreign_network_whitelist_helper("net2abc".to_string()).await; } + #[tokio::test] + async fn only_relay_peer_rpc() { + let pm_center = create_mock_peer_manager_with_mock_stun(crate::rpc::NatType::Unknown).await; + let mut flag = pm_center.get_global_ctx().get_flags(); + flag.foreign_network_whitelist = "".to_string(); + flag.relay_all_peer_rpc = true; + pm_center.get_global_ctx().config.set_flags(flag); + tracing::debug!("pm_center: {:?}", pm_center.my_peer_id()); + + let pma_net1 = create_mock_peer_manager_for_foreign_network("net1").await; + let pmb_net1 = create_mock_peer_manager_for_foreign_network("net1").await; + tracing::debug!( + "pma_net1: {:?}, pmb_net1: {:?}", + pma_net1.my_peer_id(), + pmb_net1.my_peer_id() + ); + connect_peer_manager(pma_net1.clone(), pm_center.clone()).await; + connect_peer_manager(pmb_net1.clone(), pm_center.clone()).await; + wait_route_appear(pma_net1.clone(), pmb_net1.clone()) + .await + .unwrap(); + assert_eq!(1, pma_net1.list_routes().await.len()); + assert_eq!(1, pmb_net1.list_routes().await.len()); + } + #[tokio::test] #[should_panic] async fn foreign_network_whitelist_fail() { diff --git a/easytier/src/peers/peer_manager.rs b/easytier/src/peers/peer_manager.rs index 392bcdd..f10fba6 100644 --- a/easytier/src/peers/peer_manager.rs +++ b/easytier/src/peers/peer_manager.rs @@ -309,21 +309,6 @@ impl PeerManager { self.add_client_tunnel(t).await } - fn check_network_in_whitelist(&self, network_name: &str) -> Result<(), Error> { - if self - .global_ctx - .get_flags() - .foreign_network_whitelist - .split(" ") - .map(wildmatch::WildMatch::new) - .any(|wl| wl.matches(network_name)) - { - Ok(()) - } else { - Err(anyhow::anyhow!("network {} not in whitelist", network_name).into()) - } - } - #[tracing::instrument] pub async fn add_tunnel_as_server(&self, tunnel: Box) -> Result<(), Error> { tracing::info!("add tunnel as server start"); @@ -334,7 +319,6 @@ impl PeerManager { { self.add_new_peer_conn(peer).await?; } else { - self.check_network_in_whitelist(&peer.get_network_identity().network_name)?; self.foreign_network_manager.add_peer_conn(peer).await?; } tracing::info!("add tunnel as server done");