- /api/info 新增 config_changed:与启动时的配置快照对比,启动后配置被修改(或文件缺失/解析失败)时实例卡片显示「配置发生变化」徽章,重启后自动消失 - 停止操作增加「停止中」中间态:徽章脉冲提示、按钮区禁用,实例真正停止后卡片渐隐消失,失败时自动恢复 - 停止/重启/移除确认弹窗优先显示配置名称,兜底文件名
115 lines
3.2 KiB
Rust
115 lines
3.2 KiB
Rust
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, PartialEq, Eq)]
|
|
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
|
|
}
|
|
}
|