v2
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
use crate::context::SharedNetworkAddr;
|
||||
use crate::utils::task_control::TaskGroup;
|
||||
use anyhow::Context;
|
||||
use pnet_packet::Packet;
|
||||
use pnet_packet::icmp::echo_reply::{Identifier, SequenceNumber};
|
||||
use pnet_packet::icmp::{IcmpPacket, IcmpTypes};
|
||||
use pnet_packet::ipv4::Ipv4Packet;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use tcp_ip::IpStack;
|
||||
use tcp_ip::icmp::IcmpSocket;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
pub async fn start_icmp_nat(
|
||||
task_group: &TaskGroup,
|
||||
ip_stack: &IpStack,
|
||||
no_tun: bool,
|
||||
network: SharedNetworkAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let net_icmp_socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::RAW,
|
||||
Some(socket2::Protocol::ICMPV4),
|
||||
)
|
||||
.context("new Socket RAW ICMPV4 failed")?;
|
||||
let addr: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
|
||||
net_icmp_socket
|
||||
.bind(&socket2::SockAddr::from(addr))
|
||||
.context("bind Socket ICMPV4 failed")?;
|
||||
net_icmp_socket.set_nonblocking(true)?;
|
||||
|
||||
let std_socket: std::net::UdpSocket = net_icmp_socket.into();
|
||||
|
||||
let tokio_icmp_socket = UdpSocket::from_std(std_socket)?;
|
||||
|
||||
let inner_icmp_socket = IcmpSocket::bind_all(ip_stack.clone()).await?;
|
||||
task_group.spawn(async move {
|
||||
if let Err(e) = task(tokio_icmp_socket, inner_icmp_socket, no_tun, network).await {
|
||||
log::error!("icmp task failed: {:?}", e);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
async fn task(
|
||||
tokio_icmp_socket: UdpSocket,
|
||||
inner_icmp_socket: IcmpSocket,
|
||||
no_tun: bool,
|
||||
network: SharedNetworkAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut buf1 = vec![0u8; 65536];
|
||||
let mut buf2 = vec![0u8; 65536];
|
||||
let mut map = HashMap::new();
|
||||
loop {
|
||||
tokio::select! {
|
||||
rs = tokio_icmp_socket.recv(&mut buf1) => {
|
||||
let len = rs?;
|
||||
tokio_icmp_socket_recv(&buf1[..len],&inner_icmp_socket,&map,no_tun,&network).await?;
|
||||
}
|
||||
rs = inner_icmp_socket.recv_from_to(&mut buf2) => {
|
||||
let (len,src,dst) = rs?;
|
||||
inner_icmp_socket_recv(&buf2[..len],src,dst,&tokio_icmp_socket,&mut map,no_tun,&network).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn tokio_icmp_socket_recv(
|
||||
buf: &[u8],
|
||||
inner_icmp_socket: &IcmpSocket,
|
||||
map: &HashMap<(Ipv4Addr, Identifier, SequenceNumber), Ipv4Addr>,
|
||||
no_tun: bool,
|
||||
network: &SharedNetworkAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let Some(ipv4) = Ipv4Packet::new(buf) else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(icmp) = IcmpPacket::new(ipv4.payload()) else {
|
||||
return Ok(());
|
||||
};
|
||||
if icmp.get_icmp_type() != IcmpTypes::EchoReply
|
||||
&& icmp.get_icmp_type() != IcmpTypes::EchoRequest
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let payload = icmp.payload();
|
||||
if payload.len() < 4 {
|
||||
return Ok(());
|
||||
}
|
||||
let mut src = ipv4.get_source();
|
||||
let identifier = Identifier::new(u16::from_be_bytes([payload[0], payload[1]]));
|
||||
let sequence_number = SequenceNumber::new(u16::from_be_bytes([payload[2], payload[3]]));
|
||||
let Some(dst) = map.get(&(src, identifier, sequence_number)) else {
|
||||
return Ok(());
|
||||
};
|
||||
if no_tun && src == Ipv4Addr::LOCALHOST {
|
||||
src = network.ip().context("not ip")?;
|
||||
}
|
||||
|
||||
inner_icmp_socket
|
||||
.send_from_to(ipv4.payload(), src.into(), (*dst).into())
|
||||
.await
|
||||
.context("sending ICMPv4 failed")?;
|
||||
Ok(())
|
||||
}
|
||||
async fn inner_icmp_socket_recv(
|
||||
buf: &[u8],
|
||||
src: IpAddr,
|
||||
dst: IpAddr,
|
||||
tokio_icmp_socket: &UdpSocket,
|
||||
map: &mut HashMap<(Ipv4Addr, Identifier, SequenceNumber), Ipv4Addr>,
|
||||
no_tun: bool,
|
||||
network: &SharedNetworkAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let (IpAddr::V4(src), IpAddr::V4(mut dst)) = (src, dst) else {
|
||||
return Ok(());
|
||||
};
|
||||
let Some(icmp) = IcmpPacket::new(buf) else {
|
||||
return Ok(());
|
||||
};
|
||||
if icmp.get_icmp_type() != IcmpTypes::EchoReply
|
||||
&& icmp.get_icmp_type() != IcmpTypes::EchoRequest
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let payload = icmp.payload();
|
||||
if payload.len() < 4 {
|
||||
return Ok(());
|
||||
}
|
||||
if no_tun && dst == network.ip().context("not ip")? {
|
||||
dst = Ipv4Addr::LOCALHOST;
|
||||
}
|
||||
|
||||
let identifier = Identifier::new(u16::from_be_bytes([payload[0], payload[1]]));
|
||||
let sequence_number = SequenceNumber::new(u16::from_be_bytes([payload[2], payload[3]]));
|
||||
map.insert((dst, identifier, sequence_number), src);
|
||||
tokio_icmp_socket
|
||||
.send_to(buf, SocketAddr::new(dst.into(), 0))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
use crate::context::{NetworkAddr, SharedNetworkAddr};
|
||||
use crate::nat::AllowSubnetExternalRoute;
|
||||
use crate::protocol::ip_packet_protocol::HEAD_LENGTH;
|
||||
use crate::protocol::transmission::TransmissionBytes;
|
||||
use crate::tunnel_core::outbound::HybridOutbound;
|
||||
use crate::utils::task_control::TaskGroup;
|
||||
use anyhow::Context;
|
||||
use bytes::BytesMut;
|
||||
use pnet_packet::ip::IpNextHeaderProtocol;
|
||||
use pnet_packet::ipv4::Ipv4Packet;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tcp_ip::{IpStackConfig, IpStackRecv, IpStackSend};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
mod icmp_nat;
|
||||
mod tcp_nat;
|
||||
mod udp_nat;
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct InternalNatInbound {
|
||||
no_tun: bool,
|
||||
ip_stack_send: Arc<IpStackSend>,
|
||||
allow_subnet: AllowSubnetExternalRoute,
|
||||
network: SharedNetworkAddr,
|
||||
}
|
||||
impl InternalNatInbound {
|
||||
pub async fn create(
|
||||
task_group: &TaskGroup,
|
||||
mtu: u16,
|
||||
hybrid_outbound: HybridOutbound,
|
||||
allow_subnet: AllowSubnetExternalRoute,
|
||||
network: SharedNetworkAddr,
|
||||
no_tun: bool,
|
||||
) -> anyhow::Result<Self> {
|
||||
let ip_stack_config = IpStackConfig {
|
||||
mtu,
|
||||
..Default::default()
|
||||
};
|
||||
let (ip_stack, ip_stack_send, ip_stack_recv) = tcp_ip::ip_stack(ip_stack_config)?;
|
||||
#[cfg(not(target_os = "android"))]
|
||||
icmp_nat::start_icmp_nat(task_group, &ip_stack, no_tun, network.clone()).await?;
|
||||
tcp_nat::start_tcp_nat(task_group, &ip_stack, no_tun, network.clone()).await?;
|
||||
udp_nat::start_udp_nat(task_group, &ip_stack).await?;
|
||||
task_group.spawn(async move {
|
||||
if let Err(e) = ip_stack_recv_task(ip_stack_recv, hybrid_outbound).await {
|
||||
log::error!("ip stack recv task error: {e:?}");
|
||||
}
|
||||
});
|
||||
Ok(Self {
|
||||
no_tun,
|
||||
ip_stack_send: Arc::new(ip_stack_send),
|
||||
allow_subnet,
|
||||
network,
|
||||
})
|
||||
}
|
||||
pub async fn send(&self, data: &[u8], net: &NetworkAddr) -> anyhow::Result<()> {
|
||||
if data[0] >> 4 != 4 {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(ipv4) = Ipv4Packet::new(data) else {
|
||||
return Ok(());
|
||||
};
|
||||
let dest = ipv4.get_destination();
|
||||
if net.network().contains(&dest)
|
||||
|| dest == net.broadcast
|
||||
|| dest.is_broadcast()
|
||||
|| dest.is_multicast()
|
||||
|| self.allow_subnet.allow(&dest)
|
||||
{
|
||||
self.ip_stack_send.send_ip_packet(data).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub async fn send_ipv4_payload(
|
||||
&self,
|
||||
protocol: IpNextHeaderProtocol,
|
||||
src_ip: Ipv4Addr,
|
||||
dest_ip: Ipv4Addr,
|
||||
payload: BytesMut,
|
||||
) -> anyhow::Result<()> {
|
||||
self.ip_stack_send
|
||||
.send_ipv4_payload(protocol, src_ip, dest_ip, payload)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn ip_stack_recv_task(
|
||||
mut ip_stack_recv: IpStackRecv,
|
||||
hybrid_outbound: HybridOutbound,
|
||||
) -> anyhow::Result<()> {
|
||||
loop {
|
||||
let mut bytes = TransmissionBytes::new_offset_zeroed(HEAD_LENGTH);
|
||||
let len = ip_stack_recv.recv(&mut bytes).await?;
|
||||
bytes.set_len(len)?;
|
||||
if let Err(e) = hybrid_outbound.ipv4_outbound_common(bytes).await {
|
||||
log::warn!("ip_stack_recv_task,{e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InternalNatInbound {
|
||||
fn network_contains(&self, ip: &Ipv4Addr) -> bool {
|
||||
self.network
|
||||
.network()
|
||||
.map(|net| net.contains(ip))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
pub fn use_nat(&self, dst: &Ipv4Addr) -> bool {
|
||||
if self.no_tun {
|
||||
return self.allow_nat(dst);
|
||||
}
|
||||
if self.network_contains(dst) {
|
||||
return false;
|
||||
}
|
||||
self.allow_subnet.allow(dst)
|
||||
}
|
||||
pub fn no_tun(&self) -> bool {
|
||||
self.no_tun
|
||||
}
|
||||
pub fn allow_nat(&self, dst: &Ipv4Addr) -> bool {
|
||||
self.allow_subnet.allow(dst) || self.network_contains(dst)
|
||||
}
|
||||
pub async fn tcp_nat<R, W>(
|
||||
&self,
|
||||
recv_stream: R,
|
||||
send_stream: W,
|
||||
mut dest_ip: Ipv4Addr,
|
||||
dest_port: u16,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
if self.no_tun {
|
||||
let net = self.network.get().context("no network")?;
|
||||
if dest_ip == net.ip {
|
||||
dest_ip = Ipv4Addr::LOCALHOST;
|
||||
} else if net.network().contains(&dest_ip) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let dst = SocketAddr::new(dest_ip.into(), dest_port);
|
||||
tcp_nat::stream_nat(recv_stream, send_stream, dst).await
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct PortMappingManager {
|
||||
no_tun: bool,
|
||||
allow_port_mapping: bool,
|
||||
network: SharedNetworkAddr,
|
||||
}
|
||||
|
||||
impl PortMappingManager {
|
||||
pub fn new(no_tun: bool, allow_port_mapping: bool, network: SharedNetworkAddr) -> Self {
|
||||
Self {
|
||||
no_tun,
|
||||
allow_port_mapping,
|
||||
network,
|
||||
}
|
||||
}
|
||||
pub async fn tcp_mapping<R, W>(
|
||||
&self,
|
||||
recv_stream: R,
|
||||
send_stream: W,
|
||||
dest: String,
|
||||
dest_port: u16,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
if !self.allow_port_mapping {
|
||||
log::debug!("port mapping not enabled");
|
||||
return Ok(());
|
||||
}
|
||||
if self.no_tun
|
||||
&& let Ok(dest_ip) = Ipv4Addr::from_str(&dest)
|
||||
{
|
||||
let net = self.network.get().context("no network")?;
|
||||
|
||||
if dest_ip == net.ip {
|
||||
let dst = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), dest_port);
|
||||
return tcp_nat::stream_nat(recv_stream, send_stream, dst).await;
|
||||
} else if net.network().contains(&dest_ip) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let dst = format!("{}:{}", dest, dest_port);
|
||||
tcp_nat::stream_nat(recv_stream, send_stream, dst).await
|
||||
}
|
||||
pub async fn udp_mapping<R, W>(
|
||||
&self,
|
||||
recv_stream: R,
|
||||
send_stream: W,
|
||||
dest: String,
|
||||
dest_port: u16,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
if !self.allow_port_mapping {
|
||||
log::debug!("port mapping not enabled");
|
||||
return Ok(());
|
||||
}
|
||||
if self.no_tun
|
||||
&& let Ok(dest_ip) = Ipv4Addr::from_str(&dest)
|
||||
{
|
||||
let net = self.network.get().context("no network")?;
|
||||
|
||||
if dest_ip == net.ip {
|
||||
let dst = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), dest_port);
|
||||
return udp_nat::stream_nat(recv_stream, send_stream, dst).await;
|
||||
} else if net.network().contains(&dest_ip) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let dst = format!("{}:{}", dest, dest_port);
|
||||
udp_nat::stream_nat(recv_stream, send_stream, dst).await
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use crate::context::SharedNetworkAddr;
|
||||
use crate::utils::task_control::TaskGroup;
|
||||
use anyhow::Context;
|
||||
use std::fmt::Debug;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use tcp_ip::IpStack;
|
||||
use tcp_ip::tcp::TcpListener;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::net::{TcpStream, ToSocketAddrs};
|
||||
|
||||
pub async fn start_tcp_nat(
|
||||
task_group: &TaskGroup,
|
||||
ip_stack: &IpStack,
|
||||
no_tun: bool,
|
||||
network: SharedNetworkAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let tcp_listener = TcpListener::bind_all(ip_stack.clone()).await?;
|
||||
let group = task_group.clone();
|
||||
task_group.spawn(async move {
|
||||
if let Err(e) = listen_task(&group, tcp_listener, no_tun, network).await {
|
||||
log::error!("listen task error: {:?}", e);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn listen_task(
|
||||
task_group: &TaskGroup,
|
||||
mut tcp_listener: TcpListener,
|
||||
no_tun: bool,
|
||||
network: SharedNetworkAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
loop {
|
||||
let (stream, _addr) = tcp_listener.accept().await?;
|
||||
let mut local_addr = stream.local_addr()?;
|
||||
let peer_addr = stream.peer_addr()?;
|
||||
if no_tun {
|
||||
let IpAddr::V4(ip) = local_addr.ip() else {
|
||||
continue;
|
||||
};
|
||||
if ip == network.ip().context("not ip")? {
|
||||
// 无tun的情况下写入本机的则写到localhost
|
||||
local_addr.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
}
|
||||
}
|
||||
task_group.spawn(async move {
|
||||
if let Err(e) = stream_task(stream, local_addr).await {
|
||||
log::error!("stream task Error: {:?},{peer_addr}->{local_addr}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn stream_task(
|
||||
mut inner_stream: tcp_ip::tcp::TcpStream,
|
||||
addr: SocketAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut tokio_stream = TcpStream::connect(addr).await?;
|
||||
tokio::io::copy_bidirectional(&mut inner_stream, &mut tokio_stream).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn stream_nat<R, W, A: ToSocketAddrs + Debug>(
|
||||
mut recv_stream: R,
|
||||
mut send_stream: W,
|
||||
addr: A,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
let mut tokio_stream = TcpStream::connect(&addr)
|
||||
.await
|
||||
.with_context(|| format!("error connecting to {:?}", addr))?;
|
||||
let (mut tcp_r, mut tcp_w) = tokio_stream.split();
|
||||
tokio::select! {
|
||||
_ = tokio::io::copy(&mut recv_stream, &mut tcp_w) => {},
|
||||
_ = tokio::io::copy(&mut tcp_r, &mut send_stream) => {},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
use crate::utils::task_control::TaskGroup;
|
||||
use anyhow::Context;
|
||||
use bytes::Bytes;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Debug;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use tcp_ip::IpStack;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio_util::codec::{FramedRead, FramedWrite, LengthDelimitedCodec};
|
||||
|
||||
struct NatEntry {
|
||||
socket: Arc<tokio::net::UdpSocket>,
|
||||
last_active: Instant,
|
||||
}
|
||||
|
||||
type NatTable = Arc<Mutex<HashMap<(SocketAddr, SocketAddr), NatEntry>>>;
|
||||
|
||||
const NAT_IDLE_TIMEOUT: Duration = Duration::from_secs(60 * 5);
|
||||
const NAT_GC_INTERVAL: Duration = Duration::from_secs(60);
|
||||
|
||||
pub async fn start_udp_nat(task_group: &TaskGroup, ip_stack: &IpStack) -> anyhow::Result<()> {
|
||||
let inner_socket = tcp_ip::udp::UdpSocket::bind_all(ip_stack.clone()).await?;
|
||||
let inner_socket = Arc::new(inner_socket);
|
||||
let nat_table: NatTable = Arc::new(Mutex::new(HashMap::new()));
|
||||
let mut buf = vec![0u8; 65536];
|
||||
let group = task_group.clone();
|
||||
let nat_table_clone = nat_table.clone();
|
||||
task_group.spawn(async move {
|
||||
loop {
|
||||
let (len, src, dst) = match inner_socket.recv_from_to(&mut buf).await {
|
||||
Ok(rs) => rs,
|
||||
Err(e) => {
|
||||
log::warn!("{e:?}");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) =
|
||||
handle_outbound(&group, &inner_socket, &nat_table, src, dst, &buf[..len]).await
|
||||
{
|
||||
log::warn!("udp nat outbound error: {e:?}");
|
||||
}
|
||||
}
|
||||
});
|
||||
spawn_nat_gc(task_group, nat_table_clone);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_outbound(
|
||||
task_group: &TaskGroup,
|
||||
inner: &Arc<tcp_ip::udp::UdpSocket>,
|
||||
nat: &NatTable,
|
||||
src: SocketAddr,
|
||||
dst: SocketAddr,
|
||||
packet: &[u8],
|
||||
) -> anyhow::Result<()> {
|
||||
let key = (src, dst);
|
||||
|
||||
let socket = {
|
||||
let mut table = nat.lock().await;
|
||||
if let Some(entry) = table.get_mut(&key) {
|
||||
entry.last_active = Instant::now();
|
||||
entry.socket.clone()
|
||||
} else {
|
||||
// 创建真实 UDP socket
|
||||
let sock = tokio::net::UdpSocket::bind("0.0.0.0:0").await?;
|
||||
sock.connect(dst).await?;
|
||||
let sock = Arc::new(sock);
|
||||
table.insert(
|
||||
key,
|
||||
NatEntry {
|
||||
socket: sock.clone(),
|
||||
last_active: Instant::now(),
|
||||
},
|
||||
);
|
||||
|
||||
// 启动反向转发
|
||||
spawn_inbound(
|
||||
task_group,
|
||||
inner.clone(),
|
||||
nat.clone(),
|
||||
src,
|
||||
dst,
|
||||
sock.clone(),
|
||||
);
|
||||
|
||||
sock
|
||||
}
|
||||
};
|
||||
|
||||
socket.send(packet).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn spawn_inbound(
|
||||
task_group: &TaskGroup,
|
||||
inner: Arc<tcp_ip::udp::UdpSocket>,
|
||||
nat: NatTable,
|
||||
src: SocketAddr,
|
||||
dst: SocketAddr,
|
||||
socket: Arc<tokio::net::UdpSocket>,
|
||||
) {
|
||||
task_group.spawn(async move {
|
||||
let mut buf = vec![0u8; 65536];
|
||||
|
||||
loop {
|
||||
let len = match socket.recv(&mut buf).await {
|
||||
Ok(n) => n,
|
||||
Err(_) => break,
|
||||
};
|
||||
|
||||
// 反向写回 inner socket
|
||||
if inner.send_from_to(&buf[..len], dst, src).await.is_err() {
|
||||
break;
|
||||
}
|
||||
|
||||
// 更新活跃时间
|
||||
if let Some(entry) = nat.lock().await.get_mut(&(src, dst)) {
|
||||
entry.last_active = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
// 回收 NAT
|
||||
nat.lock().await.remove(&(src, dst));
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_nat_gc(task_group: &TaskGroup, nat: NatTable) {
|
||||
task_group.spawn(async move {
|
||||
let mut interval = tokio::time::interval(NAT_GC_INTERVAL);
|
||||
|
||||
loop {
|
||||
interval.tick().await;
|
||||
|
||||
let now = Instant::now();
|
||||
let mut table = nat.lock().await;
|
||||
|
||||
table.retain(|(src, dst), entry| {
|
||||
let alive = now.duration_since(entry.last_active) < NAT_IDLE_TIMEOUT;
|
||||
if !alive {
|
||||
log::debug!("udp nat expired: {} -> {}", src, dst);
|
||||
}
|
||||
alive
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) async fn stream_nat<R, W, A: ToSocketAddrs + Debug>(
|
||||
recv_stream: R,
|
||||
send_stream: W,
|
||||
addr: A,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
R: AsyncRead + Unpin,
|
||||
W: AsyncWrite + Unpin,
|
||||
{
|
||||
let udp_socket = tokio::net::UdpSocket::bind("0.0.0.0:0").await?;
|
||||
udp_socket
|
||||
.connect(&addr)
|
||||
.await
|
||||
.with_context(|| format!("error connecting to {:?}", addr))?;
|
||||
let mut framed_read = FramedRead::new(recv_stream, LengthDelimitedCodec::new());
|
||||
let mut framed_write = FramedWrite::new(send_stream, LengthDelimitedCodec::new());
|
||||
let mut buf = vec![0u8; 65536];
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
Some(buf) = framed_read.next() => {
|
||||
let buf = buf?;
|
||||
udp_socket.send(&buf).await?;
|
||||
},
|
||||
rs = udp_socket.recv(&mut buf) =>{
|
||||
let len = rs?;
|
||||
framed_write.send(Bytes::copy_from_slice(&buf[..len])).await?;
|
||||
},
|
||||
else => {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
use ipnet::Ipv4Net;
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub(crate) mod internal_nat;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NetInput {
|
||||
pub net: Ipv4Net,
|
||||
pub target_ip: Ipv4Addr,
|
||||
}
|
||||
impl FromStr for NetInput {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let parts: Vec<&str> = s.split(',').map(|x| x.trim()).collect();
|
||||
if parts.len() != 2 {
|
||||
return Err("格式错误,应为 net,target_ip 例如: 192.168.0.0/24,10.26.0.2".into());
|
||||
}
|
||||
|
||||
let net = Ipv4Net::from_str(parts[0]).map_err(|e| format!("网络段格式错误: {}", e))?;
|
||||
|
||||
let target_ip =
|
||||
Ipv4Addr::from_str(parts[1]).map_err(|e| format!("目标 IP 格式错误: {}", e))?;
|
||||
|
||||
Ok(NetInput { net, target_ip })
|
||||
}
|
||||
}
|
||||
impl fmt::Display for NetInput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{},{}", self.net, self.target_ip)
|
||||
}
|
||||
}
|
||||
impl Serialize for NetInput {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for NetInput {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SubnetExternalRoute {
|
||||
route_table: Arc<Mutex<Vec<NetInput>>>,
|
||||
}
|
||||
impl SubnetExternalRoute {
|
||||
pub fn new(mut route_table: Vec<NetInput>) -> Self {
|
||||
route_table.sort_by_key(|r| std::cmp::Reverse(r.net.prefix_len()));
|
||||
SubnetExternalRoute {
|
||||
route_table: Arc::new(Mutex::new(route_table)),
|
||||
}
|
||||
}
|
||||
pub fn set_route_table(&self, mut route_table: Vec<NetInput>) {
|
||||
route_table.sort_by_key(|r| std::cmp::Reverse(r.net.prefix_len()));
|
||||
*self.route_table.lock() = route_table;
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Ipv4Addr> {
|
||||
let route_table = self.route_table.lock();
|
||||
if route_table.is_empty() {
|
||||
return None;
|
||||
}
|
||||
for net in route_table.iter() {
|
||||
if net.net.contains(ip) {
|
||||
return Some(net.target_ip);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn all_route(&self) -> Vec<NetInput> {
|
||||
self.route_table.lock().clone()
|
||||
}
|
||||
pub fn reset_route(&self, route_table: Vec<NetInput>) {
|
||||
*self.route_table.lock() = route_table;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AllowSubnetExternalRoute {
|
||||
route_table: Arc<Vec<Ipv4Net>>,
|
||||
}
|
||||
impl AllowSubnetExternalRoute {
|
||||
pub fn new(mut route_table: Vec<Ipv4Net>) -> Self {
|
||||
route_table.sort_by_key(|r| r.prefix_len());
|
||||
Self {
|
||||
route_table: Arc::new(route_table),
|
||||
}
|
||||
}
|
||||
pub fn allow(&self, ip: &Ipv4Addr) -> bool {
|
||||
if self.route_table.is_empty() {
|
||||
return false;
|
||||
}
|
||||
for net in self.route_table.iter() {
|
||||
if net.contains(ip) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user