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 gc_interval = tokio::time::interval(ICMP_NAT_GC_INTERVAL);
|
||||
loop {
|
||||
// 单次收发/处理失败不能拖垮整个任务:记录日志后继续,
|
||||
// 短暂休眠避免持续性错误造成空转
|
||||
tokio::select! {
|
||||
rs = tokio_icmp_socket.recv(&mut buf1) => {
|
||||
let len = rs?;
|
||||
tokio_icmp_socket_recv(&buf1[..len],&inner_icmp_socket,&mut map,no_tun,&network).await?;
|
||||
match rs {
|
||||
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) => {
|
||||
let (len,src,dst) = rs?;
|
||||
inner_icmp_socket_recv(&buf2[..len],src,dst,&tokio_icmp_socket,&mut map,no_tun,&network).await?;
|
||||
match rs {
|
||||
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() => {
|
||||
evict_expired(&mut map, Instant::now(), ICMP_NAT_TIMEOUT);
|
||||
@@ -115,7 +135,11 @@ async fn tokio_icmp_socket_recv(
|
||||
return Ok(());
|
||||
};
|
||||
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
|
||||
@@ -148,7 +172,8 @@ async fn inner_icmp_socket_recv(
|
||||
if payload.len() < 4 {
|
||||
return Ok(());
|
||||
}
|
||||
if no_tun && dst == network.ip().context("not ip")? {
|
||||
// 虚拟地址未就绪(None)时跳过重写,而不是让错误传播杀掉整个任务
|
||||
if no_tun && Some(dst) == network.ip() {
|
||||
dst = Ipv4Addr::LOCALHOST;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,14 +31,31 @@ async fn listen_task(
|
||||
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()?;
|
||||
// 单次 accept/地址查询失败不能拖垮整个监听任务:
|
||||
// 记录日志后继续,短暂休眠避免持续性错误造成空转
|
||||
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 {
|
||||
let IpAddr::V4(ip) = local_addr.ip() else {
|
||||
continue;
|
||||
};
|
||||
if ip == network.ip().context("not ip")? {
|
||||
// 虚拟地址未就绪(None)时跳过重写,而不是终止任务
|
||||
if let Some(net_ip) = network.ip()
|
||||
&& ip == net_ip
|
||||
{
|
||||
// 无tun的情况下写入本机的则写到localhost
|
||||
local_addr.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user