Files
vnt/vnt/src/channel/punch.rs
T
2023-12-30 13:07:54 +08:00

227 lines
7.3 KiB
Rust

use std::collections::HashMap;
use std::io;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::str::FromStr;
use std::time::Duration;
use rand::prelude::SliceRandom;
use crate::channel::channel::Context;
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum PunchModel {
IPv4,
IPv6,
All,
}
impl FromStr for PunchModel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().trim() {
"ipv4" => Ok(PunchModel::IPv4),
"ipv6" => Ok(PunchModel::IPv6),
_ => Ok(PunchModel::All),
}
}
}
#[derive(Clone, Debug)]
pub struct NatInfo {
pub public_ips: Vec<Ipv4Addr>,
pub public_port: u16,
pub public_port_range: u16,
pub local_ipv4_addr: SocketAddrV4,
pub ipv6_addr: SocketAddrV6,
pub nat_type: NatType,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum NatType {
Symmetric,
Cone,
}
impl NatInfo {
pub fn new(
mut public_ips: Vec<Ipv4Addr>,
public_port: u16,
public_port_range: u16,
local_ipv4_addr: SocketAddrV4,
ipv6_addr: SocketAddrV6,
mut nat_type: NatType,
) -> Self {
public_ips.retain(|ip| {
!ip.is_multicast()
&& !ip.is_broadcast()
&& !ip.is_unspecified()
&& !ip.is_loopback()
&& !ip.is_private()
});
if public_ips.len() > 1 {
nat_type = NatType::Symmetric;
}
Self {
public_ips,
public_port,
public_port_range,
local_ipv4_addr,
ipv6_addr,
nat_type,
}
}
pub fn update_addr(&mut self, ip: Ipv4Addr, port: u16) {
if !ip.is_multicast()
&& !ip.is_broadcast()
&& !ip.is_unspecified()
&& !ip.is_loopback()
&& !ip.is_private()
&& port != 0
{
self.public_port = port;
if !self.public_ips.contains(&ip) {
self.public_ips.push(ip);
}
}
}
}
#[derive(Clone)]
pub struct Punch {
context: Context,
port_vec: Vec<u16>,
port_index: HashMap<Ipv4Addr, usize>,
punch_model: PunchModel,
}
impl Punch {
pub fn new(context: Context, punch_model: PunchModel) -> Self {
let mut port_vec: Vec<u16> = (1..65535).collect();
port_vec.push(65535);
let mut rng = rand::thread_rng();
port_vec.shuffle(&mut rng);
Punch {
context,
port_vec,
port_index: HashMap::new(),
punch_model,
}
}
}
impl Punch {
pub async fn punch(&mut self, buf: &[u8], id: Ipv4Addr, nat_info: NatInfo) -> io::Result<()> {
if !self.context.need_punch(&id) {
return Ok(());
}
if !nat_info.local_ipv4_addr.ip().is_unspecified() && nat_info.local_ipv4_addr.port() != 0 {
let _ = self
.context
.send_main_udp(buf, SocketAddr::V4(nat_info.local_ipv4_addr));
}
if self.punch_model != PunchModel::IPv4
&& !nat_info.ipv6_addr.ip().is_unspecified()
&& nat_info.ipv6_addr.port() != 0
{
let rs = self
.context
.send_main_udp(buf, SocketAddr::V6(nat_info.ipv6_addr));
log::info!("发送到ipv6地址:{:?},rs={:?}", nat_info.ipv6_addr, rs);
if rs.is_ok() && self.punch_model == PunchModel::IPv6 {
return Ok(());
}
}
match nat_info.nat_type {
NatType::Symmetric => {
// 假设对方绑定n个端口,通过NAT对外映射出n个 公网ip:公网端口,自己随机尝试k次的情况下
// 猜中的概率 p = 1-((65535-n)/65535)*((65535-n-1)/(65535-1))*...*((65535-n-k+1)/(65535-k+1))
// n取76,k取600,猜中的概率就超过50%了
// 前提 自己是锥形网络,否则猜中了也通信不了
//预测范围内最多发送max_k1个包
let max_k1 = 60;
//全局最多发送max_k2个包
let max_k2 = 800;
if nat_info.public_port_range < max_k1 * 3 {
//端口变化不大时,在预测的范围内随机发送
let min_port = if nat_info.public_port > nat_info.public_port_range {
nat_info.public_port - nat_info.public_port_range
} else {
1
};
let (max_port, overflow) = nat_info
.public_port
.overflowing_add(nat_info.public_port_range);
let max_port = if overflow { 65535 } else { max_port };
let k = if max_port - min_port + 1 > max_k1 {
max_k1 as usize
} else {
(max_port - min_port + 1) as usize
};
let mut nums: Vec<u16> = (min_port..max_port).collect();
nums.push(max_port);
{
let mut rng = rand::thread_rng();
nums.shuffle(&mut rng);
}
self.punch_symmetric(&nums[..k], buf, &nat_info.public_ips, max_k1 as usize)
.await?;
}
let start = *self.port_index.entry(id.clone()).or_insert(0);
let mut end = start + max_k2;
let mut index = end;
if end >= self.port_vec.len() {
end = self.port_vec.len();
index = 0
}
self.punch_symmetric(
&self.port_vec[start..end],
buf,
&nat_info.public_ips,
max_k2,
)
.await?;
self.port_index.insert(id, index);
}
NatType::Cone => {
if nat_info.public_port != 0 {
let is_cone = self.context.is_cone();
for ip in nat_info.public_ips {
let addr = SocketAddr::V4(SocketAddrV4::new(ip, nat_info.public_port));
self.context.send_main_udp(buf, addr)?;
if !is_cone {
//只有一方是对称,则对称方要使用全部端口发送数据,符合上述计算的概率
self.context.try_send_all(buf, addr)?;
}
tokio::time::sleep(Duration::from_millis(2)).await;
}
}
}
}
Ok(())
}
async fn punch_symmetric(
&self,
ports: &[u16],
buf: &[u8],
ips: &Vec<Ipv4Addr>,
max: usize,
) -> io::Result<()> {
let mut count = 0;
for port in ports {
for pub_ip in ips {
count += 1;
if count == max {
return Ok(());
}
let addr = SocketAddr::V4(SocketAddrV4::new(*pub_ip, *port));
self.context.send_main_udp(buf, addr)?;
tokio::time::sleep(Duration::from_millis(2)).await;
}
}
Ok(())
}
}