mirror of
https://gitlab.com/veilid/veilid.git
synced 2025-02-22 23:49:50 -05:00
Improved UDP hole punching with TTL
This commit is contained in:
parent
4ce0c2ccaf
commit
7a7bb0ad2e
@ -50,6 +50,7 @@ build_cache:
|
|||||||
extends: .base
|
extends: .base
|
||||||
stage: prepare
|
stage: prepare
|
||||||
script:
|
script:
|
||||||
|
- 'echo "CI_PIPELINE_SOURCE: $CI_PIPELINE_SOURCE"'
|
||||||
- apk update && apk add jq && apk add curl
|
- apk update && apk add jq && apk add curl
|
||||||
- if ! docker manifest inspect $CI_REGISTRY_IMAGE/build-cache:latest > /dev/null; then
|
- if ! docker manifest inspect $CI_REGISTRY_IMAGE/build-cache:latest > /dev/null; then
|
||||||
- CACHE_EPOCH=0
|
- CACHE_EPOCH=0
|
||||||
|
@ -259,7 +259,6 @@ impl Settings {
|
|||||||
if let Some(ipc_path) = ipc_path.or(self.ipc_path.clone()) {
|
if let Some(ipc_path) = ipc_path.or(self.ipc_path.clone()) {
|
||||||
if is_ipc_socket_path(&ipc_path) {
|
if is_ipc_socket_path(&ipc_path) {
|
||||||
// try direct path
|
// try direct path
|
||||||
enable_network = false;
|
|
||||||
client_api_ipc_path = Some(ipc_path);
|
client_api_ipc_path = Some(ipc_path);
|
||||||
} else {
|
} else {
|
||||||
// try subnode index inside path
|
// try subnode index inside path
|
||||||
|
@ -766,10 +766,7 @@ impl NetworkManager {
|
|||||||
// Both sides will do this and then the receipt will get sent over the punched hole
|
// Both sides will do this and then the receipt will get sent over the punched hole
|
||||||
let unique_flow = network_result_try!(
|
let unique_flow = network_result_try!(
|
||||||
self.net()
|
self.net()
|
||||||
.send_data_to_dial_info(
|
.send_hole_punch(hole_punch_dial_info_detail.dial_info.clone(),)
|
||||||
hole_punch_dial_info_detail.dial_info.clone(),
|
|
||||||
Vec::new(),
|
|
||||||
)
|
|
||||||
.await?
|
.await?
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ use std::path::{Path, PathBuf};
|
|||||||
pub const UPDATE_NETWORK_CLASS_TASK_TICK_PERIOD_SECS: u32 = 1;
|
pub const UPDATE_NETWORK_CLASS_TASK_TICK_PERIOD_SECS: u32 = 1;
|
||||||
pub const NETWORK_INTERFACES_TASK_TICK_PERIOD_SECS: u32 = 1;
|
pub const NETWORK_INTERFACES_TASK_TICK_PERIOD_SECS: u32 = 1;
|
||||||
pub const UPNP_TASK_TICK_PERIOD_SECS: u32 = 1;
|
pub const UPNP_TASK_TICK_PERIOD_SECS: u32 = 1;
|
||||||
|
pub const HOLE_PUNCH_TTL: u32 = 3;
|
||||||
pub const PEEK_DETECT_LEN: usize = 64;
|
pub const PEEK_DETECT_LEN: usize = 64;
|
||||||
|
|
||||||
cfg_if! {
|
cfg_if! {
|
||||||
@ -652,6 +652,55 @@ impl Network {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send hole punch attempt to a specific dialinfo. May not be appropriate for all protocols.
|
||||||
|
// Returns a flow for the connection used to send the data
|
||||||
|
#[instrument(level = "trace", target = "net", err, skip(self))]
|
||||||
|
pub async fn send_hole_punch(
|
||||||
|
&self,
|
||||||
|
dial_info: DialInfo,
|
||||||
|
) -> EyreResult<NetworkResult<UniqueFlow>> {
|
||||||
|
let _guard = self.startup_lock.enter()?;
|
||||||
|
|
||||||
|
self.record_dial_info_failure(
|
||||||
|
dial_info.clone(),
|
||||||
|
async move {
|
||||||
|
let unique_flow;
|
||||||
|
if dial_info.protocol_type() == ProtocolType::UDP {
|
||||||
|
// Handle connectionless protocol
|
||||||
|
let peer_socket_addr = dial_info.to_socket_addr();
|
||||||
|
let ph = match self.find_best_udp_protocol_handler(&peer_socket_addr, &None) {
|
||||||
|
Some(ph) => ph,
|
||||||
|
None => {
|
||||||
|
return Ok(NetworkResult::no_connection_other(
|
||||||
|
"no appropriate UDP protocol handler for dial_info",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let flow = network_result_try!(ph
|
||||||
|
.send_hole_punch(peer_socket_addr, HOLE_PUNCH_TTL)
|
||||||
|
.await
|
||||||
|
.wrap_err("failed to send hole punch to dial info")?);
|
||||||
|
unique_flow = UniqueFlow {
|
||||||
|
flow,
|
||||||
|
connection_id: None,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return Ok(NetworkResult::ServiceUnavailable(
|
||||||
|
"unimplemented for this protocol".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Network accounting
|
||||||
|
self.network_manager()
|
||||||
|
.stats_packet_sent(dial_info.ip_addr(), ByteCount::new(0));
|
||||||
|
|
||||||
|
Ok(NetworkResult::value(unique_flow))
|
||||||
|
}
|
||||||
|
.in_current_span(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
pub async fn startup_internal(&self) -> EyreResult<StartupDisposition> {
|
pub async fn startup_internal(&self) -> EyreResult<StartupDisposition> {
|
||||||
|
@ -140,6 +140,63 @@ impl RawUdpProtocolHandler {
|
|||||||
Ok(NetworkResult::value(flow))
|
Ok(NetworkResult::value(flow))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "trace", target = "protocol", err, skip(self), fields(ret.flow))]
|
||||||
|
pub async fn send_hole_punch(
|
||||||
|
&self,
|
||||||
|
remote_addr: SocketAddr,
|
||||||
|
ttl: u32,
|
||||||
|
) -> io::Result<NetworkResult<Flow>> {
|
||||||
|
// Check to see if it is punished
|
||||||
|
if self
|
||||||
|
.network_manager()
|
||||||
|
.address_filter()
|
||||||
|
.is_ip_addr_punished(remote_addr.ip())
|
||||||
|
{
|
||||||
|
return Ok(NetworkResult::no_connection_other("punished"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get synchronous socket
|
||||||
|
let res = socket2_operation(self.socket.as_ref(), |s| {
|
||||||
|
// Get original TTL
|
||||||
|
let original_ttl = s.ttl()?;
|
||||||
|
|
||||||
|
// Set TTL
|
||||||
|
s.set_ttl(ttl)?;
|
||||||
|
|
||||||
|
// Send zero length packet
|
||||||
|
let res = s.send_to(&[], &remote_addr.into());
|
||||||
|
|
||||||
|
// Restore TTL immediately
|
||||||
|
s.set_ttl(original_ttl)?;
|
||||||
|
|
||||||
|
res
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check for errors
|
||||||
|
let len = network_result_try!(res.into_network_result()?);
|
||||||
|
if len != 0 {
|
||||||
|
bail_io_error_other!("wrong size send");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a flow for the sent message
|
||||||
|
let peer_addr = PeerAddress::new(
|
||||||
|
SocketAddress::from_socket_addr(remote_addr),
|
||||||
|
ProtocolType::UDP,
|
||||||
|
);
|
||||||
|
let local_socket_addr = self.socket.local_addr()?;
|
||||||
|
|
||||||
|
let flow = Flow::new(
|
||||||
|
peer_addr,
|
||||||
|
SocketAddress::from_socket_addr(local_socket_addr),
|
||||||
|
);
|
||||||
|
|
||||||
|
log_net!("udp::send_hole_punch: {:?}", flow);
|
||||||
|
|
||||||
|
#[cfg(feature = "verbose-tracing")]
|
||||||
|
tracing::Span::current().record("ret.flow", format!("{:?}", flow).as_str());
|
||||||
|
Ok(NetworkResult::value(flow))
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(level = "trace", target = "protocol", err)]
|
#[instrument(level = "trace", target = "protocol", err)]
|
||||||
pub async fn new_unspecified_bound_handler(
|
pub async fn new_unspecified_bound_handler(
|
||||||
registry: VeilidComponentRegistry,
|
registry: VeilidComponentRegistry,
|
||||||
|
@ -806,13 +806,10 @@ impl NetworkManager {
|
|||||||
// punch should come through and create a real 'last connection' for us if this succeeds
|
// punch should come through and create a real 'last connection' for us if this succeeds
|
||||||
network_result_try!(
|
network_result_try!(
|
||||||
self.net()
|
self.net()
|
||||||
.send_data_to_dial_info(hole_punch_did.dial_info.clone(), Vec::new())
|
.send_hole_punch(hole_punch_did.dial_info.clone())
|
||||||
.await?
|
.await?
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add small delay to encourage packets to be delivered in order
|
|
||||||
sleep(HOLE_PUNCH_DELAY_MS).await;
|
|
||||||
|
|
||||||
// Issue the signal
|
// Issue the signal
|
||||||
let rpc = self.rpc_processor();
|
let rpc = self.rpc_processor();
|
||||||
network_result_try!(rpc
|
network_result_try!(rpc
|
||||||
@ -826,13 +823,6 @@ impl NetworkManager {
|
|||||||
.await
|
.await
|
||||||
.wrap_err("failed to send signal")?);
|
.wrap_err("failed to send signal")?);
|
||||||
|
|
||||||
// Another hole punch after the signal for UDP redundancy
|
|
||||||
network_result_try!(
|
|
||||||
self.net()
|
|
||||||
.send_data_to_dial_info(hole_punch_did.dial_info, Vec::new())
|
|
||||||
.await?
|
|
||||||
);
|
|
||||||
|
|
||||||
// Wait for the return receipt
|
// Wait for the return receipt
|
||||||
let inbound_nr = match eventual_value
|
let inbound_nr = match eventual_value
|
||||||
.timeout_at(stop_token)
|
.timeout_at(stop_token)
|
||||||
|
@ -5,3 +5,6 @@
|
|||||||
# Avoid committing pubspec.lock for library packages; see
|
# Avoid committing pubspec.lock for library packages; see
|
||||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
|
|
||||||
|
.flutter-plugins/
|
||||||
|
.flutter-plugins-dependencies/
|
||||||
|
@ -31,6 +31,12 @@ fn io_error_kind_from_error<T>(e: io::Error) -> io::Result<NetworkResult<T>> {
|
|||||||
return Ok(NetworkResult::NoConnection(e));
|
return Ok(NetworkResult::NoConnection(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
if let Some(os_err) = e.raw_os_error() {
|
||||||
|
if os_err == winapi::um::winsock2::WSAENETRESET {
|
||||||
|
return Ok(NetworkResult::NoConnection(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout),
|
io::ErrorKind::TimedOut => Ok(NetworkResult::Timeout),
|
||||||
io::ErrorKind::UnexpectedEof
|
io::ErrorKind::UnexpectedEof
|
||||||
|
@ -98,7 +98,7 @@ pub fn set_tcp_stream_linger(
|
|||||||
unsafe {
|
unsafe {
|
||||||
let s = socket2::Socket::from_raw_fd(tcp_stream.as_raw_fd());
|
let s = socket2::Socket::from_raw_fd(tcp_stream.as_raw_fd());
|
||||||
let res = s.set_linger(linger);
|
let res = s.set_linger(linger);
|
||||||
s.into_raw_fd();
|
let _ = s.into_raw_fd();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ pub fn set_tcp_stream_linger(
|
|||||||
unsafe {
|
unsafe {
|
||||||
let s = socket2::Socket::from_raw_socket(tcp_stream.as_raw_socket());
|
let s = socket2::Socket::from_raw_socket(tcp_stream.as_raw_socket());
|
||||||
let res = s.set_linger(linger);
|
let res = s.set_linger(linger);
|
||||||
s.into_raw_socket();
|
let _ = s.into_raw_socket();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,3 +282,36 @@ async fn nonblocking_connect(
|
|||||||
pub fn domain_for_address(address: SocketAddr) -> core::ffi::c_int {
|
pub fn domain_for_address(address: SocketAddr) -> core::ffi::c_int {
|
||||||
socket2::Domain::for_address(address).into()
|
socket2::Domain::for_address(address).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run operations on underlying socket
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(unix)] {
|
||||||
|
pub fn socket2_operation<S: std::os::fd::AsRawFd, F: FnOnce(&mut socket2::Socket) -> R, R>(
|
||||||
|
s: &S,
|
||||||
|
callback: F,
|
||||||
|
) -> R {
|
||||||
|
use std::os::fd::{FromRawFd, IntoRawFd};
|
||||||
|
let mut s = unsafe { socket2::Socket::from_raw_fd(s.as_raw_fd()) };
|
||||||
|
let res = callback(&mut s);
|
||||||
|
let _ = s.into_raw_fd();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
} else if #[cfg(windows)] {
|
||||||
|
pub fn socket2_operation<
|
||||||
|
S: std::os::windows::io::AsRawSocket,
|
||||||
|
F: FnOnce(&mut socket2::Socket) -> R,
|
||||||
|
R,
|
||||||
|
>(
|
||||||
|
s: &S,
|
||||||
|
callback: F,
|
||||||
|
) -> R {
|
||||||
|
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
|
||||||
|
let mut s = unsafe { socket2::Socket::from_raw_socket(s.as_raw_socket()) };
|
||||||
|
let res = callback(&mut s);
|
||||||
|
let _ = s.into_raw_socket();
|
||||||
|
res
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
#[compile_error("unimplemented")]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user