nat4-nat4 punch (#388)

this patch optimize the udp hole punch logic:

1. allow start punch hole before stun test complete.
2. add lock to symmetric punch, avoid conflict between concurrent hole punching task.
3. support punching hole for predictable nat4-nat4.
4. make backoff of retry reasonable
This commit is contained in:
Sijie.Sun
2024-10-06 22:49:18 +08:00
committed by GitHub
parent ba3da97ad4
commit 37ceb77bf6
24 changed files with 2748 additions and 1310 deletions
+4
View File
@@ -42,6 +42,8 @@ message RpcPacket {
int32 trace_id = 9;
}
message Void {}
message UUID {
uint64 high = 1;
uint64 low = 2;
@@ -57,6 +59,8 @@ enum NatType {
PortRestricted = 5;
Symmetric = 6;
SymUdpFirewall = 7;
SymmetricEasyInc = 8;
SymmetricEasyDec = 9;
}
message Ipv4Addr { uint32 addr = 1; }
+68 -17
View File
@@ -93,27 +93,78 @@ service DirectConnectorRpc {
rpc GetIpList(GetIpListRequest) returns (GetIpListResponse);
}
message TryPunchHoleRequest { common.SocketAddr local_mapped_addr = 1; }
message TryPunchHoleResponse { common.SocketAddr remote_mapped_addr = 1; }
message TryPunchSymmetricRequest {
common.SocketAddr listener_addr = 1;
uint32 port = 2;
repeated common.Ipv4Addr public_ips = 3;
uint32 min_port = 4;
uint32 max_port = 5;
uint32 transaction_id = 6;
uint32 round = 7;
uint32 last_port_index = 8;
message SelectPunchListenerRequest {
bool force_new = 1;
}
message TryPunchSymmetricResponse { uint32 last_port_index = 1; }
message SelectPunchListenerResponse {
common.SocketAddr listener_mapped_addr = 1;
}
message SendPunchPacketConeRequest {
common.SocketAddr listener_mapped_addr = 1;
common.SocketAddr dest_addr = 2;
uint32 transaction_id = 3;
// send this many packets in a batch
uint32 packet_count_per_batch = 4;
// send total this batch count, total packet count = packet_batch_size * packet_batch_count
uint32 packet_batch_count = 5;
// interval between each batch
uint32 packet_interval_ms = 6;
}
message SendPunchPacketHardSymRequest {
common.SocketAddr listener_mapped_addr = 1;
repeated common.Ipv4Addr public_ips = 2;
uint32 transaction_id = 3;
uint32 port_index = 4;
uint32 round = 5;
}
message SendPunchPacketHardSymResponse { uint32 next_port_index = 1; }
message SendPunchPacketEasySymRequest {
common.SocketAddr listener_mapped_addr = 1;
repeated common.Ipv4Addr public_ips = 2;
uint32 transaction_id = 3;
uint32 base_port_num = 4;
uint32 max_port_num = 5;
bool is_incremental = 6;
}
message SendPunchPacketBothEasySymRequest {
uint32 udp_socket_count = 1;
common.Ipv4Addr public_ip = 2;
uint32 transaction_id = 3;
uint32 dst_port_num = 4;
uint32 wait_time_ms = 5;
}
message SendPunchPacketBothEasySymResponse {
// is doing punch with other peer
bool is_busy = 1;
common.SocketAddr base_mapped_addr = 2;
}
service UdpHolePunchRpc {
rpc TryPunchHole(TryPunchHoleRequest) returns (TryPunchHoleResponse);
rpc TryPunchSymmetric(TryPunchSymmetricRequest)
returns (TryPunchSymmetricResponse);
rpc SelectPunchListener(SelectPunchListenerRequest)
returns (SelectPunchListenerResponse);
// send packet to one remote_addr, used by nat1-3 to nat1-3
rpc SendPunchPacketCone(SendPunchPacketConeRequest) returns (common.Void);
// send packet to multiple remote_addr (birthday attack), used by nat4 to nat1-3
rpc SendPunchPacketHardSym(SendPunchPacketHardSymRequest)
returns (SendPunchPacketHardSymResponse);
rpc SendPunchPacketEasySym(SendPunchPacketEasySymRequest)
returns (common.Void);
// nat4 to nat4 (both predictably)
rpc SendPunchPacketBothEasySym(SendPunchPacketBothEasySymRequest)
returns (SendPunchPacketBothEasySymResponse);
}
message DirectConnectedPeerInfo { int32 latency_ms = 1; }
+1 -1
View File
@@ -146,7 +146,7 @@ impl Server {
async fn handle_rpc_request(packet: RpcPacket, reg: Arc<ServiceRegistry>) -> Result<Bytes> {
let rpc_request = RpcRequest::decode(Bytes::from(packet.body))?;
let timeout_duration = std::time::Duration::from_millis(rpc_request.timeout_ms as u64);
let ctrl = RpcController {};
let ctrl = RpcController::default();
Ok(timeout(
timeout_duration,
reg.call_method(
+30 -2
View File
@@ -13,6 +13,34 @@ pub trait Controller: Send + Sync + 'static {
}
#[derive(Debug)]
pub struct BaseController {}
pub struct BaseController {
pub timeout_ms: i32,
pub trace_id: i32,
}
impl Controller for BaseController {}
impl Controller for BaseController {
fn timeout_ms(&self) -> i32 {
self.timeout_ms
}
fn set_timeout_ms(&mut self, timeout_ms: i32) {
self.timeout_ms = timeout_ms;
}
fn set_trace_id(&mut self, trace_id: i32) {
self.trace_id = trace_id;
}
fn trace_id(&self) -> i32 {
self.trace_id
}
}
impl Default for BaseController {
fn default() -> Self {
Self {
timeout_ms: 5000,
trace_id: 0,
}
}
}
+6 -6
View File
@@ -121,14 +121,14 @@ async fn rpc_basic_test() {
// small size req and resp
let ctrl = RpcController {};
let ctrl = RpcController::default();
let input = SayHelloRequest {
name: "world".to_string(),
};
let ret = out.say_hello(ctrl, input).await;
assert_eq!(ret.unwrap().greeting, "Hello world!");
let ctrl = RpcController {};
let ctrl = RpcController::default();
let input = SayGoodbyeRequest {
name: "world".to_string(),
};
@@ -136,7 +136,7 @@ async fn rpc_basic_test() {
assert_eq!(ret.unwrap().greeting, "Goodbye, world!");
// large size req and resp
let ctrl = RpcController {};
let ctrl = RpcController::default();
let name = random_string(20 * 1024 * 1024);
let input = SayGoodbyeRequest { name: name.clone() };
let ret = out.say_goodbye(ctrl, input).await;
@@ -160,7 +160,7 @@ async fn rpc_timeout_test() {
.client
.scoped_client::<GreetingClientFactory<RpcController>>(1, 1, "test".to_string());
let ctrl = RpcController {};
let ctrl = RpcController::default();
let input = SayHelloRequest {
name: "world".to_string(),
};
@@ -199,7 +199,7 @@ async fn standalone_rpc_test() {
.await
.unwrap();
let ctrl = RpcController {};
let ctrl = RpcController::default();
let input = SayHelloRequest {
name: "world".to_string(),
};
@@ -211,7 +211,7 @@ async fn standalone_rpc_test() {
.await
.unwrap();
let ctrl = RpcController {};
let ctrl = RpcController::default();
let input = SayGoodbyeRequest {
name: "world".to_string(),
};