mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-06 17:59:11 +00:00
allow loopback src address in listener (#1730)
This commit is contained in:
@@ -303,6 +303,18 @@ impl GlobalCtx {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_port_in_running_listeners(&self, port: u16, is_udp: bool) -> bool {
|
||||
let check_proto = |listener_proto: &str| {
|
||||
let listener_is_udp = matches!(listener_proto, "udp" | "wg");
|
||||
listener_is_udp == is_udp
|
||||
};
|
||||
self.running_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|x| x.port() == Some(port) && check_proto(x.scheme()))
|
||||
}
|
||||
|
||||
pub fn get_vpn_portal_cidr(&self) -> Option<cidr::Ipv4Cidr> {
|
||||
self.config.get_vpn_portal_config().map(|x| x.client_cidr)
|
||||
}
|
||||
|
||||
@@ -507,6 +507,15 @@ impl KcpProxyDst {
|
||||
Some(dst_socket.ip()) == global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip.address()));
|
||||
|
||||
if send_to_self && global_ctx.no_tun() {
|
||||
if global_ctx.is_port_in_running_listeners(dst_socket.port(), false)
|
||||
&& global_ctx.is_ip_in_same_network(&src_ip)
|
||||
{
|
||||
return Err(anyhow::anyhow!(
|
||||
"dst socket {:?} is in running listeners, ignore it",
|
||||
dst_socket
|
||||
)
|
||||
.into());
|
||||
}
|
||||
dst_socket = format!("127.0.0.1:{}", dst_socket.port()).parse().unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -416,6 +416,15 @@ impl QUICProxyDst {
|
||||
|
||||
let send_to_self = Some(*dst_socket.ip()) == ctx.get_ipv4().map(|ip| ip.address());
|
||||
if send_to_self && ctx.no_tun() {
|
||||
if ctx.is_port_in_running_listeners(dst_socket.port(), false)
|
||||
&& ctx.is_ip_in_same_network(&src_ip)
|
||||
{
|
||||
return Err(anyhow::anyhow!(
|
||||
"dst socket {:?} is in running listeners, ignore it",
|
||||
dst_socket
|
||||
)
|
||||
.into());
|
||||
}
|
||||
dst_socket = format!("127.0.0.1:{}", dst_socket.port()).parse().unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -733,6 +733,18 @@ impl<C: NatDstConnector> TcpProxy<C> {
|
||||
let nat_dst = if Some(nat_entry.real_dst.ip())
|
||||
== global_ctx.get_ipv4().map(|ip| IpAddr::V4(ip.address()))
|
||||
{
|
||||
if global_ctx.is_port_in_running_listeners(nat_entry.real_dst.port(), false)
|
||||
&& global_ctx.is_ip_in_same_network(&nat_entry.src.ip())
|
||||
{
|
||||
tracing::error!(
|
||||
?nat_entry,
|
||||
"nat dst port {} is in running listeners, ignore it",
|
||||
nat_entry.real_dst.port()
|
||||
);
|
||||
nat_entry.state.store(NatDstEntryState::Closed);
|
||||
Self::remove_entry_from_all_conn_map(conn_map, addr_conn_map, nat_entry);
|
||||
return;
|
||||
}
|
||||
format!("127.0.0.1:{}", nat_entry.real_dst.port())
|
||||
.parse()
|
||||
.unwrap()
|
||||
|
||||
@@ -298,6 +298,30 @@ impl UdpProxy {
|
||||
udp::UdpPacket::new(ipv4.payload())?
|
||||
};
|
||||
|
||||
// TODO: should it be async.
|
||||
let dst_socket = if Some(ipv4.get_destination())
|
||||
== self.global_ctx.get_ipv4().as_ref().map(Ipv4Inet::address)
|
||||
{
|
||||
if self
|
||||
.global_ctx
|
||||
.is_port_in_running_listeners(udp_packet.get_destination(), true)
|
||||
&& self
|
||||
.global_ctx
|
||||
.is_ip_in_same_network(&std::net::IpAddr::V4(ipv4.get_source()))
|
||||
{
|
||||
tracing::debug!(
|
||||
dst_port = udp_packet.get_destination(),
|
||||
"dst socket is in running listeners, ignore it"
|
||||
);
|
||||
return Some(());
|
||||
}
|
||||
format!("127.0.0.1:{}", udp_packet.get_destination())
|
||||
.parse()
|
||||
.unwrap()
|
||||
} else {
|
||||
SocketAddr::new(real_dst_ip.into(), udp_packet.get_destination())
|
||||
};
|
||||
|
||||
tracing::trace!(
|
||||
?packet,
|
||||
?ipv4,
|
||||
@@ -339,17 +363,6 @@ impl UdpProxy {
|
||||
|
||||
nat_entry.mark_active();
|
||||
|
||||
// TODO: should it be async.
|
||||
let dst_socket = if Some(ipv4.get_destination())
|
||||
== self.global_ctx.get_ipv4().as_ref().map(Ipv4Inet::address)
|
||||
{
|
||||
format!("127.0.0.1:{}", udp_packet.get_destination())
|
||||
.parse()
|
||||
.unwrap()
|
||||
} else {
|
||||
SocketAddr::new(real_dst_ip.into(), udp_packet.get_destination())
|
||||
};
|
||||
|
||||
let send_ret = {
|
||||
let _g = self.global_ctx.net_ns.guard();
|
||||
nat_entry
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||
net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
||||
sync::{atomic::AtomicBool, Arc, Weak},
|
||||
time::{Instant, SystemTime},
|
||||
};
|
||||
@@ -416,47 +416,25 @@ impl PeerManager {
|
||||
if src.scheme() == "ring" {
|
||||
return Ok(());
|
||||
}
|
||||
let src_host = match src.socket_addrs(|| Some(1)) {
|
||||
Ok(addrs) => addrs,
|
||||
Err(_) => {
|
||||
// if the tunnel is not rely on ip address, skip check
|
||||
return Ok(());
|
||||
}
|
||||
let Ok(Some(addr)) = src.socket_addrs(|| Some(1)).map(|x| x.first().cloned()) else {
|
||||
// if the tunnel is not rely on ip address, skip check
|
||||
return Ok(());
|
||||
};
|
||||
let virtual_ipv4 = self.global_ctx.get_ipv4().map(|ip| ip.network());
|
||||
let virtual_ipv6 = self.global_ctx.get_ipv6().map(|ip| ip.network());
|
||||
tracing::info!(
|
||||
?virtual_ipv4,
|
||||
?virtual_ipv6,
|
||||
"check remote addr not from virtual network"
|
||||
);
|
||||
for addr in src_host {
|
||||
// if no-tun is enabled, the src ip of packet in virtual network is converted to loopback address
|
||||
if addr.ip().is_loopback()
|
||||
&& !self
|
||||
.allow_loopback_tunnel
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
anyhow::bail!("tunnel src host is loopback address");
|
||||
}
|
||||
|
||||
match addr {
|
||||
SocketAddr::V4(addr) => {
|
||||
if let Some(virtual_ipv4) = virtual_ipv4 {
|
||||
if virtual_ipv4.contains(addr.ip()) {
|
||||
anyhow::bail!("tunnel src host is from the virtual network (ignore this error please)");
|
||||
}
|
||||
}
|
||||
}
|
||||
SocketAddr::V6(addr) => {
|
||||
if let Some(virtual_ipv6) = virtual_ipv6 {
|
||||
if virtual_ipv6.contains(addr.ip()) {
|
||||
anyhow::bail!("tunnel src host is from the virtual network (ignore this error please)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if no-tun is enabled, the src ip of packet in virtual network is converted to loopback address
|
||||
// we already filter out the connection in tcp/quic/kcp proxy so no need check here.
|
||||
if addr.ip().is_loopback() {
|
||||
// allow other loopback address, good for conn from cdn/l4 connection
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.global_ctx.is_ip_in_same_network(&addr.ip()) {
|
||||
anyhow::bail!(
|
||||
"tunnel src {} is from the same network (ignore this error please)",
|
||||
addr
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1481,9 +1481,23 @@ pub async fn relay_bps_limit_test(#[values(100, 200, 400, 800)] bps_limit: u64)
|
||||
drop_insts(insts).await;
|
||||
}
|
||||
|
||||
#[rstest::rstest]
|
||||
#[serial_test::serial]
|
||||
#[tokio::test]
|
||||
async fn avoid_tunnel_loop_back_to_virtual_network() {
|
||||
let insts = init_three_node("udp").await;
|
||||
async fn avoid_tunnel_loop_back_to_virtual_network(#[values(true, false)] no_tun: bool) {
|
||||
let insts = init_three_node_ex(
|
||||
"udp",
|
||||
|cfg| {
|
||||
if matches!(cfg.get_inst_name().as_str(), "inst2" | "inst3") {
|
||||
let mut flags = cfg.get_flags();
|
||||
flags.no_tun = no_tun;
|
||||
cfg.set_flags(flags);
|
||||
}
|
||||
cfg
|
||||
},
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
|
||||
let tcp_connector = TcpTunnelConnector::new("tcp://10.144.144.2:11010".parse().unwrap());
|
||||
insts[0]
|
||||
|
||||
Reference in New Issue
Block a user