fix(nat): TCP/ICMP NAT 不再因单点瞬时错误永久瘫痪
问题: - tcp_nat listen_task:accept() 或 local/peer_addr 查询失败、 no_tun 下 network.ip() 未就绪(None),任一错误经 ? 传播直接 杀掉整个监听任务,NAT 永久失效且无重启。 - icmp_nat task:任一侧 socket 收发或单包处理出错同样经 ? 传播 终止整个任务。 修复: - 循环内错误改为 log::warn + continue,并加 100ms 休眠避免持续性 错误造成空转。 - no_tun 下 network.ip() 为 None 时跳过重写/丢弃该包,而非报错退出。 注:该修复为异步 I/O 循环的容错行为变更,依赖真实 socket, 未补充端到端测试;已有 nat 相关单测全部通过。
This commit is contained in:
@@ -60,14 +60,34 @@ async fn task(
|
|||||||
let mut map: IcmpNatMap = HashMap::new();
|
let mut map: IcmpNatMap = HashMap::new();
|
||||||
let mut gc_interval = tokio::time::interval(ICMP_NAT_GC_INTERVAL);
|
let mut gc_interval = tokio::time::interval(ICMP_NAT_GC_INTERVAL);
|
||||||
loop {
|
loop {
|
||||||
|
// 单次收发/处理失败不能拖垮整个任务:记录日志后继续,
|
||||||
|
// 短暂休眠避免持续性错误造成空转
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
rs = tokio_icmp_socket.recv(&mut buf1) => {
|
rs = tokio_icmp_socket.recv(&mut buf1) => {
|
||||||
let len = rs?;
|
match rs {
|
||||||
tokio_icmp_socket_recv(&buf1[..len],&inner_icmp_socket,&mut map,no_tun,&network).await?;
|
Ok(len) => {
|
||||||
|
if let Err(e) = tokio_icmp_socket_recv(&buf1[..len],&inner_icmp_socket,&mut map,no_tun,&network).await {
|
||||||
|
log::warn!("icmp nat outbound error: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("icmp nat recv error: {e:?}");
|
||||||
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rs = inner_icmp_socket.recv_from_to(&mut buf2) => {
|
rs = inner_icmp_socket.recv_from_to(&mut buf2) => {
|
||||||
let (len,src,dst) = rs?;
|
match rs {
|
||||||
inner_icmp_socket_recv(&buf2[..len],src,dst,&tokio_icmp_socket,&mut map,no_tun,&network).await?;
|
Ok((len,src,dst)) => {
|
||||||
|
if let Err(e) = inner_icmp_socket_recv(&buf2[..len],src,dst,&tokio_icmp_socket,&mut map,no_tun,&network).await {
|
||||||
|
log::warn!("icmp nat inbound error: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("icmp nat inner recv error: {e:?}");
|
||||||
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ = gc_interval.tick() => {
|
_ = gc_interval.tick() => {
|
||||||
evict_expired(&mut map, Instant::now(), ICMP_NAT_TIMEOUT);
|
evict_expired(&mut map, Instant::now(), ICMP_NAT_TIMEOUT);
|
||||||
@@ -115,7 +135,11 @@ async fn tokio_icmp_socket_recv(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
if no_tun && src == Ipv4Addr::LOCALHOST {
|
if no_tun && src == Ipv4Addr::LOCALHOST {
|
||||||
src = network.ip().context("not ip")?;
|
// 虚拟地址未就绪时丢弃该应答包,而不是让错误传播杀掉整个任务
|
||||||
|
let Some(ip) = network.ip() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
src = ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
inner_icmp_socket
|
inner_icmp_socket
|
||||||
@@ -148,7 +172,8 @@ async fn inner_icmp_socket_recv(
|
|||||||
if payload.len() < 4 {
|
if payload.len() < 4 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
if no_tun && dst == network.ip().context("not ip")? {
|
// 虚拟地址未就绪(None)时跳过重写,而不是让错误传播杀掉整个任务
|
||||||
|
if no_tun && Some(dst) == network.ip() {
|
||||||
dst = Ipv4Addr::LOCALHOST;
|
dst = Ipv4Addr::LOCALHOST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,14 +31,31 @@ async fn listen_task(
|
|||||||
network: SharedNetworkAddr,
|
network: SharedNetworkAddr,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
loop {
|
loop {
|
||||||
let (stream, _addr) = tcp_listener.accept().await?;
|
// 单次 accept/地址查询失败不能拖垮整个监听任务:
|
||||||
let mut local_addr = stream.local_addr()?;
|
// 记录日志后继续,短暂休眠避免持续性错误造成空转
|
||||||
let peer_addr = stream.peer_addr()?;
|
let (stream, _addr) = match tcp_listener.accept().await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("tcp nat accept error: {e:?}");
|
||||||
|
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (mut local_addr, peer_addr) = match (stream.local_addr(), stream.peer_addr()) {
|
||||||
|
(Ok(local_addr), Ok(peer_addr)) => (local_addr, peer_addr),
|
||||||
|
(Err(e), _) | (_, Err(e)) => {
|
||||||
|
log::warn!("tcp nat get addr error: {e:?}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
if no_tun {
|
if no_tun {
|
||||||
let IpAddr::V4(ip) = local_addr.ip() else {
|
let IpAddr::V4(ip) = local_addr.ip() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if ip == network.ip().context("not ip")? {
|
// 虚拟地址未就绪(None)时跳过重写,而不是终止任务
|
||||||
|
if let Some(net_ip) = network.ip()
|
||||||
|
&& ip == net_ip
|
||||||
|
{
|
||||||
// 无tun的情况下写入本机的则写到localhost
|
// 无tun的情况下写入本机的则写到localhost
|
||||||
local_addr.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
local_addr.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user