Feat/web (Patchset 4) (#460)

support basic functions in frontend
1. create/del network
2. inspect network running status
This commit is contained in:
Sijie.Sun
2024-11-08 23:33:17 +08:00
committed by GitHub
parent 8aca5851f2
commit e948dbfcc1
64 changed files with 11671 additions and 344 deletions

View File

@@ -9,6 +9,28 @@ use serde::{Deserialize, Serialize};
use crate::tunnel::generate_digest_from_str;
pub type Flags = crate::proto::common::FlagsInConfig;
pub fn gen_default_flags() -> Flags {
Flags {
default_protocol: "tcp".to_string(),
dev_name: "".to_string(),
enable_encryption: true,
enable_ipv6: true,
mtu: 1380,
latency_first: false,
enable_exit_node: false,
no_tun: false,
use_smoltcp: false,
foreign_network_whitelist: "*".to_string(),
disable_p2p: false,
relay_all_peer_rpc: false,
disable_udp_hole_punching: false,
ipv6_listener: "udp://[::]:0".to_string(),
multi_thread: false,
}
}
#[auto_impl::auto_impl(Box, &)]
pub trait ConfigLoader: Send + Sync {
fn get_id(&self) -> uuid::Uuid;
@@ -127,7 +149,7 @@ pub struct PeerConfig {
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub struct NetworkConfig {
pub struct ProxyNetworkConfig {
pub cidr: String,
pub allow: Option<Vec<String>>,
}
@@ -150,42 +172,6 @@ pub struct VpnPortalConfig {
pub wireguard_listen: SocketAddr,
}
// Flags is used to control the behavior of the program
#[derive(derivative::Derivative, Deserialize, Serialize)]
#[derivative(Debug, Clone, PartialEq, Default)]
pub struct Flags {
#[derivative(Default(value = "\"tcp\".to_string()"))]
pub default_protocol: String,
#[derivative(Default(value = "\"\".to_string()"))]
pub dev_name: String,
#[derivative(Default(value = "true"))]
pub enable_encryption: bool,
#[derivative(Default(value = "true"))]
pub enable_ipv6: bool,
#[derivative(Default(value = "1380"))]
pub mtu: u16,
#[derivative(Default(value = "false"))]
pub latency_first: bool,
#[derivative(Default(value = "false"))]
pub enable_exit_node: bool,
#[derivative(Default(value = "false"))]
pub no_tun: bool,
#[derivative(Default(value = "false"))]
pub use_smoltcp: bool,
#[derivative(Default(value = "\"*\".to_string()"))]
pub foreign_network_whitelist: String,
#[derivative(Default(value = "false"))]
pub disable_p2p: bool,
#[derivative(Default(value = "false"))]
pub relay_all_peer_rpc: bool,
#[derivative(Default(value = "false"))]
pub disable_udp_hole_punching: bool,
#[derivative(Default(value = "\"udp://[::]:0\".to_string()"))]
pub ipv6_listener: String,
#[derivative(Default(value = "false"))]
pub multi_thread: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
struct Config {
netns: Option<String>,
@@ -199,7 +185,7 @@ struct Config {
exit_nodes: Option<Vec<Ipv4Addr>>,
peer: Option<Vec<PeerConfig>>,
proxy_network: Option<Vec<NetworkConfig>>,
proxy_network: Option<Vec<ProxyNetworkConfig>>,
file_logger: Option<FileLoggerConfig>,
console_logger: Option<ConsoleLoggerConfig>,
@@ -255,7 +241,7 @@ impl TomlConfigLoader {
}
fn gen_flags(mut flags_hashmap: serde_json::Map<String, serde_json::Value>) -> Flags {
let default_flags_json = serde_json::to_string(&Flags::default()).unwrap();
let default_flags_json = serde_json::to_string(&gen_default_flags()).unwrap();
let default_flags_hashmap =
serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(&default_flags_json)
.unwrap();
@@ -372,7 +358,7 @@ impl ConfigLoader for TomlConfigLoader {
.proxy_network
.as_mut()
.unwrap()
.push(NetworkConfig {
.push(ProxyNetworkConfig {
cidr: cidr_str,
allow: None,
});
@@ -527,7 +513,7 @@ impl ConfigLoader for TomlConfigLoader {
}
fn dump(&self) -> String {
let default_flags_json = serde_json::to_string(&Flags::default()).unwrap();
let default_flags_json = serde_json::to_string(&gen_default_flags()).unwrap();
let default_flags_hashmap =
serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(&default_flags_json)
.unwrap();

View File

@@ -4,7 +4,7 @@
extern crate rust_i18n;
use std::{
net::{Ipv4Addr, SocketAddr},
net::{Ipv4Addr, SocketAddr},
path::PathBuf,
};
@@ -505,7 +505,7 @@ impl From<Cli> for TomlConfigLoader {
f.latency_first = cli.latency_first;
f.dev_name = cli.dev_name.unwrap_or_default();
if let Some(mtu) = cli.mtu {
f.mtu = mtu;
f.mtu = mtu as u32;
}
f.enable_exit_node = cli.enable_exit_node;
f.no_tun = cli.no_tun || cfg!(not(feature = "tun"));
@@ -653,13 +653,13 @@ pub fn handle_event(mut events: EventBusSubscriber) -> tokio::task::JoinHandle<(
}
#[cfg(target_os = "windows")]
fn win_service_event_loop(
fn win_service_event_loop(
stop_notify: std::sync::Arc<tokio::sync::Notify>,
inst: launcher::NetworkInstance,
status_handle: windows_service::service_control_handler::ServiceStatusHandle,
) {
use tokio::runtime::Runtime;
inst: launcher::NetworkInstance,
status_handle: windows_service::service_control_handler::ServiceStatusHandle,
) {
use std::time::Duration;
use tokio::runtime::Runtime;
use windows_service::service::*;
std::thread::spawn(move || {
@@ -699,26 +699,23 @@ fn win_service_event_loop(
#[cfg(target_os = "windows")]
fn win_service_main(_: Vec<std::ffi::OsString>) {
use std::time::Duration;
use windows_service::service_control_handler::*;
use windows_service::service::*;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Notify;
use windows_service::service::*;
use windows_service::service_control_handler::*;
let cli = Cli::parse();
let cfg = TomlConfigLoader::from(cli);
init_logger(&cfg, false).unwrap();
init_logger(&cfg, false).unwrap();
let stop_notify_send = Arc::new(Notify::new());
let stop_notify_recv = Arc::clone(&stop_notify_send);
let event_handler = move |control_event| -> ServiceControlHandlerResult {
match control_event {
ServiceControl::Interrogate => {
ServiceControlHandlerResult::NoError
}
ServiceControl::Stop =>
{
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
ServiceControl::Stop => {
stop_notify_send.notify_one();
ServiceControlHandlerResult::NoError
}
@@ -736,10 +733,12 @@ fn win_service_main(_: Vec<std::ffi::OsString>) {
process_id: None,
};
let mut inst = launcher::NetworkInstance::new(cfg).set_fetch_node_info(false);
inst.start().unwrap();
status_handle.set_service_status(next_status).expect("set service status fail");
win_service_event_loop(stop_notify_recv, inst, status_handle);
inst.start().unwrap();
status_handle
.set_service_status(next_status)
.expect("set service status fail");
win_service_event_loop(stop_notify_recv, inst, status_handle);
}
#[tokio::main]
@@ -750,18 +749,19 @@ async fn main() {
#[cfg(target_os = "windows")]
match windows_service::service_dispatcher::start(String::new(), ffi_service_main) {
Ok(_) => std::thread::park(),
Err(e) =>
{
let should_panic = if let windows_service::Error::Winapi(ref io_error) = e {
io_error.raw_os_error() != Some(0x427) // ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
} else { true };
if should_panic {
panic!("SCM start an error: {}", e);
}
}
};
Err(e) => {
let should_panic = if let windows_service::Error::Winapi(ref io_error) = e {
io_error.raw_os_error() != Some(0x427) // ERROR_FAILED_SERVICE_CONTROLLER_CONNECT
} else {
true
};
if should_panic {
panic!("SCM start an error: {}", e);
}
}
};
let cli = Cli::parse();
setup_panic_handler();

View File

@@ -5,7 +5,10 @@ use std::{
use crate::{
common::{
config::{ConfigLoader, TomlConfigLoader},
config::{
gen_default_flags, ConfigLoader, NetworkIdentity, PeerConfig, TomlConfigLoader,
VpnPortalConfig,
},
constants::EASYTIER_VERSION,
global_ctx::{EventBusSubscriber, GlobalCtxEvent},
stun::StunInfoCollectorTrait,
@@ -14,6 +17,7 @@ use crate::{
peers::rpc_service::PeerManagerRpcService,
proto::cli::{list_peer_route_pair, PeerInfo, Route},
};
use anyhow::Context;
use chrono::{DateTime, Local};
use tokio::{sync::broadcast, task::JoinSet};
@@ -388,3 +392,132 @@ impl NetworkInstance {
}
}
}
pub type NetworkingMethod = crate::proto::web::NetworkingMethod;
pub type NetworkConfig = crate::proto::web::NetworkConfig;
impl NetworkConfig {
pub fn gen_config(&self) -> Result<TomlConfigLoader, anyhow::Error> {
let cfg = TomlConfigLoader::default();
cfg.set_id(
self.instance_id
.clone()
.unwrap_or(uuid::Uuid::new_v4().to_string())
.parse()
.with_context(|| format!("failed to parse instance id: {:?}", self.instance_id))?,
);
cfg.set_hostname(self.hostname.clone());
cfg.set_dhcp(self.dhcp.unwrap_or_default());
cfg.set_inst_name(self.network_name.clone().unwrap_or_default());
cfg.set_network_identity(NetworkIdentity::new(
self.network_name.clone().unwrap_or_default(),
self.network_secret.clone().unwrap_or_default(),
));
if !cfg.get_dhcp() {
let virtual_ipv4 = self.virtual_ipv4.clone().unwrap_or_default();
if virtual_ipv4.len() > 0 {
let ip = format!("{}/{}", virtual_ipv4, self.network_length.unwrap_or(24))
.parse()
.with_context(|| {
format!(
"failed to parse ipv4 inet address: {}, {:?}",
virtual_ipv4, self.network_length
)
})?;
cfg.set_ipv4(Some(ip));
}
}
match NetworkingMethod::try_from(self.networking_method.unwrap_or_default())
.unwrap_or_default()
{
NetworkingMethod::PublicServer => {
let public_server_url = self.public_server_url.clone().unwrap_or_default();
cfg.set_peers(vec![PeerConfig {
uri: public_server_url.parse().with_context(|| {
format!("failed to parse public server uri: {}", public_server_url)
})?,
}]);
}
NetworkingMethod::Manual => {
let mut peers = vec![];
for peer_url in self.peer_urls.iter() {
if peer_url.is_empty() {
continue;
}
peers.push(PeerConfig {
uri: peer_url
.parse()
.with_context(|| format!("failed to parse peer uri: {}", peer_url))?,
});
}
cfg.set_peers(peers);
}
NetworkingMethod::Standalone => {}
}
let mut listener_urls = vec![];
for listener_url in self.listener_urls.iter() {
if listener_url.is_empty() {
continue;
}
listener_urls.push(
listener_url
.parse()
.with_context(|| format!("failed to parse listener uri: {}", listener_url))?,
);
}
cfg.set_listeners(listener_urls);
for n in self.proxy_cidrs.iter() {
cfg.add_proxy_cidr(
n.parse()
.with_context(|| format!("failed to parse proxy network: {}", n))?,
);
}
cfg.set_rpc_portal(
format!("0.0.0.0:{}", self.rpc_port.unwrap_or_default())
.parse()
.with_context(|| format!("failed to parse rpc portal port: {:?}", self.rpc_port))?,
);
if self.enable_vpn_portal.unwrap_or_default() {
let cidr = format!(
"{}/{}",
self.vpn_portal_client_network_addr
.clone()
.unwrap_or_default(),
self.vpn_portal_client_network_len.unwrap_or(24)
);
cfg.set_vpn_portal_config(VpnPortalConfig {
client_cidr: cidr
.parse()
.with_context(|| format!("failed to parse vpn portal client cidr: {}", cidr))?,
wireguard_listen: format!(
"0.0.0.0:{}",
self.vpn_portal_listen_port.unwrap_or_default()
)
.parse()
.with_context(|| {
format!(
"failed to parse vpn portal wireguard listen port. {:?}",
self.vpn_portal_listen_port
)
})?,
});
}
let mut flags = gen_default_flags();
if let Some(latency_first) = self.latency_first {
flags.latency_first = latency_first;
}
if let Some(dev_name) = self.dev_name.clone() {
flags.dev_name = dev_name;
}
cfg.set_flags(flags);
Ok(cfg)
}
}

View File

@@ -4,6 +4,24 @@ import "error.proto";
package common;
message FlagsInConfig {
string default_protocol = 1;
string dev_name = 2;
bool enable_encryption = 3;
bool enable_ipv6 = 4;
uint32 mtu = 5;
bool latency_first = 6;
bool enable_exit_node = 7;
bool no_tun = 8;
bool use_smoltcp = 9;
string foreign_network_whitelist = 10;
bool disable_p2p = 11;
bool relay_all_peer_rpc = 12;
bool disable_udp_hole_punching = 13;
string ipv6_listener = 14;
bool multi_thread = 15;
}
message RpcDescriptor {
// allow same service registered multiple times in different domain
string domain_name = 1;
@@ -45,8 +63,10 @@ message RpcPacket {
message Void {}
message UUID {
uint64 high = 1;
uint64 low = 2;
uint32 part1 = 1;
uint32 part2 = 2;
uint32 part3 = 3;
uint32 part4 = 4;
}
enum NatType {

View File

@@ -7,13 +7,21 @@ include!(concat!(env!("OUT_DIR"), "/common.rs"));
impl From<uuid::Uuid> for Uuid {
fn from(uuid: uuid::Uuid) -> Self {
let (high, low) = uuid.as_u64_pair();
Uuid { low, high }
Uuid {
part1: (high >> 32) as u32,
part2: (high & 0xFFFFFFFF) as u32,
part3: (low >> 32) as u32,
part4: (low & 0xFFFFFFFF) as u32,
}
}
}
impl From<Uuid> for uuid::Uuid {
fn from(uuid: Uuid) -> Self {
uuid::Uuid::from_u64_pair(uuid.high, uuid.low)
uuid::Uuid::from_u64_pair(
(u64::from(uuid.part1) << 32) | u64::from(uuid.part2),
(u64::from(uuid.part3) << 32) | u64::from(uuid.part4),
)
}
}

View File

@@ -6,6 +6,42 @@ import "cli.proto";
package web;
enum NetworkingMethod {
PublicServer = 0;
Manual = 1;
Standalone = 2;
}
message NetworkConfig {
optional string instance_id = 1;
optional bool dhcp = 2;
optional string virtual_ipv4 = 3;
optional int32 network_length = 4;
optional string hostname = 5;
optional string network_name = 6;
optional string network_secret = 7;
optional NetworkingMethod networking_method = 8;
optional string public_server_url = 9;
repeated string peer_urls = 10;
repeated string proxy_cidrs = 11;
optional bool enable_vpn_portal = 12;
optional int32 vpn_portal_listen_port = 13;
optional string vpn_portal_client_network_addr = 14;
optional int32 vpn_portal_client_network_len = 15;
optional bool advanced_settings = 16;
repeated string listener_urls = 17;
optional int32 rpc_port = 18;
optional bool latency_first = 19;
optional string dev_name = 20;
}
message MyNodeInfo {
common.Ipv4Addr virtual_ipv4 = 1;
string hostname = 2;
@@ -52,10 +88,11 @@ service WebServerService {
}
message ValidateConfigRequest {
string config = 1;
NetworkConfig config = 1;
}
message ValidateConfigResponse {
string toml_config = 1;
}
message RunNetworkInstanceRequest {

View File

@@ -234,13 +234,13 @@ mod tests {
async fn ipv6_domain_pingpong() {
let listener = QUICTunnelListener::new("quic://[::1]:31016".parse().unwrap());
let mut connector =
QUICTunnelConnector::new("quic://test.kkrainbow.top:31016".parse().unwrap());
QUICTunnelConnector::new("quic://test.easytier.top:31016".parse().unwrap());
connector.set_ip_version(IpVersion::V6);
_tunnel_pingpong(listener, connector).await;
let listener = QUICTunnelListener::new("quic://127.0.0.1:31016".parse().unwrap());
let mut connector =
QUICTunnelConnector::new("quic://test.kkrainbow.top:31016".parse().unwrap());
QUICTunnelConnector::new("quic://test.easytier.top:31016".parse().unwrap());
connector.set_ip_version(IpVersion::V4);
_tunnel_pingpong(listener, connector).await;
}

View File

@@ -248,13 +248,13 @@ mod tests {
async fn ipv6_domain_pingpong() {
let listener = TcpTunnelListener::new("tcp://[::1]:31015".parse().unwrap());
let mut connector =
TcpTunnelConnector::new("tcp://test.kkrainbow.top:31015".parse().unwrap());
TcpTunnelConnector::new("tcp://test.easytier.top:31015".parse().unwrap());
connector.set_ip_version(IpVersion::V6);
_tunnel_pingpong(listener, connector).await;
let listener = TcpTunnelListener::new("tcp://127.0.0.1:31015".parse().unwrap());
let mut connector =
TcpTunnelConnector::new("tcp://test.kkrainbow.top:31015".parse().unwrap());
TcpTunnelConnector::new("tcp://test.easytier.top:31015".parse().unwrap());
connector.set_ip_version(IpVersion::V4);
_tunnel_pingpong(listener, connector).await;
}

View File

@@ -905,13 +905,13 @@ mod tests {
async fn ipv6_domain_pingpong() {
let listener = UdpTunnelListener::new("udp://[::1]:31016".parse().unwrap());
let mut connector =
UdpTunnelConnector::new("udp://test.kkrainbow.top:31016".parse().unwrap());
UdpTunnelConnector::new("udp://test.easytier.top:31016".parse().unwrap());
connector.set_ip_version(IpVersion::V6);
_tunnel_pingpong(listener, connector).await;
let listener = UdpTunnelListener::new("udp://127.0.0.1:31016".parse().unwrap());
let mut connector =
UdpTunnelConnector::new("udp://test.kkrainbow.top:31016".parse().unwrap());
UdpTunnelConnector::new("udp://test.easytier.top:31016".parse().unwrap());
connector.set_ip_version(IpVersion::V4);
_tunnel_pingpong(listener, connector).await;
}

View File

@@ -895,14 +895,14 @@ pub mod tests {
let (server_cfg, client_cfg) = create_wg_config();
let listener = WgTunnelListener::new("wg://[::1]:31016".parse().unwrap(), server_cfg);
let mut connector =
WgTunnelConnector::new("wg://test.kkrainbow.top:31016".parse().unwrap(), client_cfg);
WgTunnelConnector::new("wg://test.easytier.top:31016".parse().unwrap(), client_cfg);
connector.set_ip_version(IpVersion::V6);
_tunnel_pingpong(listener, connector).await;
let (server_cfg, client_cfg) = create_wg_config();
let listener = WgTunnelListener::new("wg://127.0.0.1:31016".parse().unwrap(), server_cfg);
let mut connector =
WgTunnelConnector::new("wg://test.kkrainbow.top:31016".parse().unwrap(), client_cfg);
WgTunnelConnector::new("wg://test.easytier.top:31016".parse().unwrap(), client_cfg);
connector.set_ip_version(IpVersion::V4);
_tunnel_pingpong(listener, connector).await;
}

View File

@@ -91,8 +91,8 @@ impl WebClientService for Controller {
_: BaseController,
req: ValidateConfigRequest,
) -> Result<ValidateConfigResponse, rpc_types::error::Error> {
let _ = TomlConfigLoader::new_from_str(&req.config)?;
Ok(ValidateConfigResponse {})
let toml_config = req.config.unwrap_or_default().gen_config()?.dump();
Ok(ValidateConfigResponse { toml_config })
}
async fn run_network_instance(