Implement prefer IPv6 option for the client (#376)

* implement prefer_ipv6 option

* implement prefer_ipv6 option

* remove unused prefer_ipv6

* add prefer_ipv6 to sample config

* fix merge

* remove unused prefer_ipv6

* move prefer_piv6 to correct position in full.toml

* set prefer_ipv6 default value

* run clippy
This commit is contained in:
Dennis Streicher
2025-06-17 18:11:08 +02:00
committed by GitHub
parent be14d124a2
commit c4a7893b09
4 changed files with 43 additions and 10 deletions

View File

@@ -227,7 +227,7 @@ async fn run_data_channel<T: Transport>(args: Arc<RunDataChannelArgs<T>>) -> Res
if args.service.service_type != ServiceType::Udp {
bail!("Expect UDP traffic. Please check the configuration.")
}
run_data_channel_for_udp::<T>(conn, &args.service.local_addr).await?;
run_data_channel_for_udp::<T>(conn, &args.service.local_addr, args.service.prefer_ipv6).await?;
}
}
Ok(())
@@ -255,7 +255,7 @@ async fn run_data_channel_for_tcp<T: Transport>(
type UdpPortMap = Arc<RwLock<HashMap<SocketAddr, mpsc::Sender<Bytes>>>>;
#[instrument(skip(conn))]
async fn run_data_channel_for_udp<T: Transport>(conn: T::Stream, local_addr: &str) -> Result<()> {
async fn run_data_channel_for_udp<T: Transport>(conn: T::Stream, local_addr: &str, prefer_ipv6: bool) -> Result<()> {
debug!("New data channel starts forwarding");
let port_map: UdpPortMap = Arc::new(RwLock::new(HashMap::new()));
@@ -305,7 +305,7 @@ async fn run_data_channel_for_udp<T: Transport>(conn: T::Stream, local_addr: &st
// grabbing the writer lock
let mut m = port_map.write().await;
match udp_connect(local_addr).await {
match udp_connect(local_addr, prefer_ipv6).await {
Ok(s) => {
let (inbound_tx, inbound_rx) = mpsc::channel(UDP_SENDQ_SIZE);
m.insert(packet.from, inbound_tx);

View File

@@ -63,6 +63,8 @@ pub struct ClientServiceConfig {
#[serde(skip)]
pub name: String,
pub local_addr: String,
#[serde(default)] // Default to false
pub prefer_ipv6: bool,
pub token: Option<MaskedString>,
pub nodelay: Option<bool>,
pub retry_interval: Option<u64>,
@@ -201,6 +203,7 @@ fn default_client_retry_interval() -> u64 {
pub struct ClientConfig {
pub remote_addr: String,
pub default_token: Option<MaskedString>,
pub prefer_ipv6: Option<bool>,
pub services: HashMap<String, ClientServiceConfig>,
#[serde(default)]
pub transport: TransportConfig,

View File

@@ -64,16 +64,45 @@ pub fn host_port_pair(s: &str) -> Result<(&str, u16)> {
}
/// Create a UDP socket and connect to `addr`
pub async fn udp_connect<A: ToSocketAddrs>(addr: A) -> Result<UdpSocket> {
let addr = to_socket_addr(addr).await?;
pub async fn udp_connect<A: ToSocketAddrs>(addr: A, prefer_ipv6: bool) -> Result<UdpSocket> {
let bind_addr = match addr {
SocketAddr::V4(_) => "0.0.0.0:0",
SocketAddr::V6(_) => ":::0",
let (socket_addr, bind_addr);
match prefer_ipv6 {
false => {
socket_addr = to_socket_addr(addr).await?;
bind_addr = match socket_addr {
SocketAddr::V4(_) => "0.0.0.0:0",
SocketAddr::V6(_) => ":::0",
};
},
true => {
let all_host_addresses: Vec<SocketAddr> = lookup_host(addr).await?.collect();
// Try to find an IPv6 address
match all_host_addresses.clone().iter().find(|x| x.is_ipv6()) {
Some(socket_addr_ipv6) => {
socket_addr = *socket_addr_ipv6;
bind_addr = ":::0";
},
None => {
let socket_addr_ipv4 = all_host_addresses.iter().find(|x| x.is_ipv4());
match socket_addr_ipv4 {
None => return Err(anyhow!("Failed to lookup the host")),
// fallback to IPv4
Some(socket_addr_ipv4) => {
socket_addr = *socket_addr_ipv4;
bind_addr = "0.0.0.0:0";
}
}
}
}
}
};
let s = UdpSocket::bind(bind_addr).await?;
s.connect(addr).await?;
s.connect(socket_addr).await?;
s.connect(socket_addr).await?;
Ok(s)
}

View File

@@ -17,6 +17,7 @@ remote_public_key = "key_encoded_in_base64" # Optional
[client.services.service1] # A service that needs forwarding. The name `service1` can change arbitrarily, as long as identical to the name in the server's configuration
type = "tcp" # Optional. The protocol that needs forwarding. Possible values: ["tcp", "udp"]. Default: "tcp"
token = "whatever" # Necessary if `client.default_token` not set
prefer_ipv6 = false # Optional. If the client prefers to use IPv6 when connecting to the server (e.g.: When the client is behind an ISP's NAT). Default: false
local_addr = "127.0.0.1:1081" # Necessary. The address of the service that needs to be forwarded
[client.services.service2] # Multiple services can be defined