This commit is contained in:
lbl
2026-02-10 18:20:39 +08:00
parent 71da72da34
commit b4301a8106
261 changed files with 20179 additions and 21547 deletions
+139
View File
@@ -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(())
}
+225
View File
@@ -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
}
}
+81
View File
@@ -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(())
}
+188
View File
@@ -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(())
}
+114
View File
@@ -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
}
}