feat: add TAP device mode and Ethernet interoperability
This commit is contained in:
+70
-9
@@ -4,7 +4,7 @@ use ipnet::Ipv4Net;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::{Path, PathBuf};
|
||||
use vnt_core::context::config::Config;
|
||||
use vnt_core::context::config::{Config, DeviceMode};
|
||||
use vnt_core::nat::NetInput;
|
||||
use vnt_core::tls::verifier::CertValidationMode;
|
||||
use vnt_core::tunnel_core::server::transport::config::ProtocolAddress;
|
||||
@@ -23,7 +23,9 @@ pub struct FileConfig {
|
||||
pub input: Option<Vec<NetInput>>,
|
||||
pub output: Option<Vec<Ipv4Net>>,
|
||||
pub no_nat: Option<bool>,
|
||||
pub no_tun: Option<bool>,
|
||||
pub device_mode: Option<DeviceMode>,
|
||||
#[serde(rename = "no_tun", skip_serializing)]
|
||||
pub legacy_no_tun: Option<bool>,
|
||||
pub mtu: Option<u16>,
|
||||
pub ctrl_port: Option<u16>,
|
||||
pub port_mapping: Option<Vec<String>>,
|
||||
@@ -134,9 +136,9 @@ pub struct Args {
|
||||
/// 关闭内置子网NAT
|
||||
#[clap(long)]
|
||||
pub no_nat: bool,
|
||||
/// 禁用tun,禁用后只能充当流量出口或者进行端口映射,无需管理员权限
|
||||
/// 虚拟网卡模式:no(无网卡)、tun(三层网卡)、tap(二层网卡)
|
||||
#[clap(long)]
|
||||
pub no_tun: bool,
|
||||
pub device_mode: Option<DeviceMode>,
|
||||
/// 端口映射,格式为:协议://本地监听地址-目标虚拟IP-目标映射地址
|
||||
#[clap(long)]
|
||||
pub port_mapping: Vec<PortMapping>,
|
||||
@@ -177,6 +179,14 @@ pub fn build_config_from_args_and_file(
|
||||
args: Option<Args>,
|
||||
file: Option<FileConfig>,
|
||||
) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
if file
|
||||
.as_ref()
|
||||
.is_some_and(|config| config.legacy_no_tun.is_some())
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"configuration key 'no_tun' was removed; use device_mode = \"no|tun|tap\""
|
||||
));
|
||||
}
|
||||
match (args, file) {
|
||||
(Some(args), Some(file)) => build_from_args_and_file(args, file),
|
||||
(Some(args), None) => build_from_args_only(args),
|
||||
@@ -258,7 +268,7 @@ fn build_from_args_and_file(args: Args, file: FileConfig) -> anyhow::Result<(Con
|
||||
input,
|
||||
output,
|
||||
no_nat: args.no_nat || file.no_nat.unwrap_or(false),
|
||||
no_tun: args.no_tun || file.no_tun.unwrap_or(false),
|
||||
device_mode: args.device_mode.or(file.device_mode).unwrap_or_default(),
|
||||
mtu: args.mtu.or(file.mtu),
|
||||
port_mapping,
|
||||
allow_port_mapping: args.allow_mapping || file.allow_mapping.unwrap_or(false),
|
||||
@@ -299,7 +309,7 @@ fn build_from_args_only(args: Args) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
.unwrap_or(CertValidationMode::InsecureSkipVerification),
|
||||
output: args.output,
|
||||
no_nat: args.no_nat,
|
||||
no_tun: args.no_tun,
|
||||
device_mode: args.device_mode.unwrap_or_default(),
|
||||
mtu: args.mtu,
|
||||
port_mapping: args.port_mapping,
|
||||
allow_port_mapping: args.allow_mapping,
|
||||
@@ -313,6 +323,11 @@ fn build_from_args_only(args: Args) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
}
|
||||
|
||||
fn build_from_file_only(file: FileConfig) -> anyhow::Result<(Config, CtrlConfig)> {
|
||||
if file.legacy_no_tun.is_some() {
|
||||
return Err(anyhow!(
|
||||
"configuration key 'no_tun' was removed; use device_mode = \"no|tun|tap\""
|
||||
));
|
||||
}
|
||||
let server_addr = file.to_server_addr()?;
|
||||
let port_mapping = file.to_port_mapping()?;
|
||||
|
||||
@@ -358,7 +373,7 @@ fn build_from_file_only(file: FileConfig) -> anyhow::Result<(Config, CtrlConfig)
|
||||
cert_mode,
|
||||
output: file.output.unwrap_or_default(),
|
||||
no_nat: file.no_nat.unwrap_or(false),
|
||||
no_tun: file.no_tun.unwrap_or(false),
|
||||
device_mode: file.device_mode.unwrap_or_default(),
|
||||
mtu: file.mtu,
|
||||
port_mapping,
|
||||
allow_port_mapping: file.allow_mapping.unwrap_or(false),
|
||||
@@ -422,8 +437,8 @@ server = ["quic://1.2.3.4:29872"]
|
||||
# 是否关闭内置子网NAT,关闭(设为true)后需要配置网卡转发,否则无法使用点对网。通常关闭内置子网NAT,使用系统的网卡转发,点对网性能会更好
|
||||
# no_nat = false
|
||||
|
||||
# 是否关闭TUN虚拟网卡,关闭(设为true)后只能充当流量出口或者进行端口映射,关闭后无需管理员权限
|
||||
# no_tun = false
|
||||
# 虚拟网卡模式:no(无网卡)、tun(三层网卡,默认)、tap(二层网卡)
|
||||
# device_mode = "tun"
|
||||
|
||||
# 端口映射,格式为:协议://本地监听地址-目标虚拟IP-目标映射地址
|
||||
# 端口映射用于在本地监听指定端口,并将收到的网络流量经由指定虚拟节点转发到目标地址,从而实现跨网络或内网服务访问
|
||||
@@ -512,4 +527,50 @@ mod tests {
|
||||
assert_eq!(config.tunnel_port, Some(12345));
|
||||
assert_eq!(config.outbound_interface.as_deref(), Some("Ethernet"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_device_mode_cli_and_legacy_rejection() {
|
||||
let args = Args::try_parse_from([
|
||||
"vnt",
|
||||
"-s",
|
||||
"quic://127.0.0.1:29872",
|
||||
"-n",
|
||||
"test-net",
|
||||
"--device-mode",
|
||||
"tap",
|
||||
])
|
||||
.unwrap();
|
||||
let (config, _) = build_from_args_only(args).unwrap();
|
||||
assert_eq!(config.device_mode, DeviceMode::Tap);
|
||||
|
||||
let legacy: FileConfig = toml::from_str("no_tun = true").unwrap();
|
||||
let error = match build_config_from_args_and_file(None, Some(legacy)) {
|
||||
Err(error) => error,
|
||||
Ok(_) => panic!("legacy no_tun must be rejected"),
|
||||
};
|
||||
assert!(error.to_string().contains("device_mode"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_device_mode_cli_overrides_file_and_file_defaults() {
|
||||
let file: FileConfig = toml::from_str("device_mode = \"no\"").unwrap();
|
||||
let args = Args::try_parse_from(["vnt", "-s", "quic://127.0.0.1:29872", "-n", "test-net"])
|
||||
.unwrap();
|
||||
let (config, _) = build_config_from_args_and_file(Some(args), Some(file)).unwrap();
|
||||
assert_eq!(config.device_mode, DeviceMode::No);
|
||||
|
||||
let file: FileConfig = toml::from_str("device_mode = \"no\"").unwrap();
|
||||
let args = Args::try_parse_from([
|
||||
"vnt",
|
||||
"-s",
|
||||
"quic://127.0.0.1:29872",
|
||||
"-n",
|
||||
"test-net",
|
||||
"--device-mode",
|
||||
"tap",
|
||||
])
|
||||
.unwrap();
|
||||
let (config, _) = build_config_from_args_and_file(Some(args), Some(file)).unwrap();
|
||||
assert_eq!(config.device_mode, DeviceMode::Tap);
|
||||
}
|
||||
}
|
||||
|
||||
+14
-6
@@ -104,18 +104,26 @@ async fn main0() -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
};
|
||||
if !network_manager.is_no_tun() {
|
||||
log::info!("启动网络:{}/{}", reg_msg.ip, reg_msg.prefix_len);
|
||||
network_manager.start_tun().await.context("start tun")?;
|
||||
if network_manager.device_mode().has_device() {
|
||||
log::info!(
|
||||
"启动网络:{}/{} ({})",
|
||||
reg_msg.ip,
|
||||
reg_msg.prefix_len,
|
||||
network_manager.device_mode()
|
||||
);
|
||||
network_manager
|
||||
.set_tun_network_ip(reg_msg.ip, reg_msg.prefix_len)
|
||||
.start_device()
|
||||
.await
|
||||
.context("start device")?;
|
||||
network_manager
|
||||
.set_device_network_ip(reg_msg.ip, reg_msg.prefix_len)
|
||||
.await
|
||||
.context("set network ip")?;
|
||||
if !sub_input.is_empty() {
|
||||
let if_index = network_manager
|
||||
.tun_if_index()
|
||||
.device_if_index()
|
||||
.await
|
||||
.context("tun_if_index")?;
|
||||
.context("device_if_index")?;
|
||||
let mut route_manager = route_manager::RouteManager::new()?;
|
||||
for x in sub_input {
|
||||
let route = Route::new(x.net.network().into(), x.net.prefix_len())
|
||||
|
||||
Reference in New Issue
Block a user