Android Support (#166)

1. Add vpnservice tauri plugin for android.
2. add workflow for android.
3. Easytier Core support android, allow set tun fd.
This commit is contained in:
Sijie.Sun
2024-07-15 00:03:55 +08:00
committed by GitHub
parent 4938e3ed2b
commit 858ade2eee
113 changed files with 3847 additions and 537 deletions

View File

@@ -150,7 +150,7 @@ pub struct VpnPortalConfig {
pub struct Flags {
#[derivative(Default(value = "\"tcp\".to_string()"))]
pub default_protocol: String,
#[derivative(Default(value = "\"tun\".to_string()"))]
#[derivative(Default(value = "\"\".to_string()"))]
pub dev_name: String,
#[derivative(Default(value = "true"))]
pub enable_encryption: bool,

View File

@@ -9,28 +9,40 @@ use super::error::Error;
pub trait IfConfiguerTrait: Send + Sync {
async fn add_ipv4_route(
&self,
name: &str,
address: Ipv4Addr,
cidr_prefix: u8,
) -> Result<(), Error>;
_name: &str,
_address: Ipv4Addr,
_cidr_prefix: u8,
) -> Result<(), Error> {
Ok(())
}
async fn remove_ipv4_route(
&self,
name: &str,
address: Ipv4Addr,
cidr_prefix: u8,
) -> Result<(), Error>;
_name: &str,
_address: Ipv4Addr,
_cidr_prefix: u8,
) -> Result<(), Error> {
Ok(())
}
async fn add_ipv4_ip(
&self,
name: &str,
address: Ipv4Addr,
cidr_prefix: u8,
) -> Result<(), Error>;
async fn set_link_status(&self, name: &str, up: bool) -> Result<(), Error>;
async fn remove_ip(&self, name: &str, ip: Option<Ipv4Addr>) -> Result<(), Error>;
_name: &str,
_address: Ipv4Addr,
_cidr_prefix: u8,
) -> Result<(), Error> {
Ok(())
}
async fn set_link_status(&self, _name: &str, _up: bool) -> Result<(), Error> {
Ok(())
}
async fn remove_ip(&self, _name: &str, _ip: Option<Ipv4Addr>) -> Result<(), Error> {
Ok(())
}
async fn wait_interface_show(&self, _name: &str) -> Result<(), Error> {
return Ok(());
}
async fn set_mtu(&self, _name: &str, _mtu: u32) -> Result<(), Error>;
async fn set_mtu(&self, _name: &str, _mtu: u32) -> Result<(), Error> {
Ok(())
}
}
fn cidr_to_subnet_mask(prefix_length: u8) -> Ipv4Addr {
@@ -379,6 +391,10 @@ impl IfConfiguerTrait for WindowsIfConfiger {
}
}
pub struct DummyIfConfiger {}
#[async_trait]
impl IfConfiguerTrait for DummyIfConfiger {}
#[cfg(target_os = "macos")]
pub type IfConfiger = MacIfConfiger;
@@ -387,3 +403,6 @@ pub type IfConfiger = LinuxIfConfiger;
#[cfg(target_os = "windows")]
pub type IfConfiger = WindowsIfConfiger;
#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
pub type IfConfiger = DummyIfConfiger;

View File

@@ -15,6 +15,13 @@ struct InterfaceFilter {
iface: NetworkInterface,
}
#[cfg(target_os = "android")]
impl InterfaceFilter {
async fn filter_iface(&self) -> bool {
true
}
}
#[cfg(target_os = "linux")]
impl InterfaceFilter {
async fn is_tun_tap_device(&self) -> bool {

View File

@@ -24,6 +24,10 @@ async fn set_bind_addr_for_peer_connector(
is_ipv4: bool,
ip_collector: &Arc<IPCollector>,
) {
if cfg!(target_os = "android") {
return;
}
let ips = ip_collector.collect_ip_addrs().await;
if is_ipv4 {
let mut bind_addrs = vec![];

View File

@@ -172,8 +172,8 @@ and the vpn client is in network of 10.14.14.0/24"
#[arg(long, help = "do not use ipv6", default_value = "false")]
disable_ipv6: bool,
#[arg(long, help = "interface name", default_value = "tun")]
dev_name: String,
#[arg(long, help = "optional tun interface name")]
dev_name: Option<String>,
#[arg(
long,
@@ -427,7 +427,7 @@ impl From<Cli> for TomlConfigLoader {
f.enable_encryption = !cli.disable_encryption;
f.enable_ipv6 = !cli.disable_ipv6;
f.latency_first = cli.latency_first;
f.dev_name = cli.dev_name;
f.dev_name = cli.dev_name.unwrap_or(Default::default());
if let Some(mtu) = cli.mtu {
f.mtu = mtu;
}

View File

@@ -214,7 +214,7 @@ impl Instance {
let peer_manager_c = self.peer_manager.clone();
let global_ctx_c = self.get_global_ctx();
let nic_ctx = self.nic_ctx.clone();
let peer_packet_receiver = self.peer_packet_receiver.clone();
let _peer_packet_receiver = self.peer_packet_receiver.clone();
tokio::spawn(async move {
let default_ipv4_addr = Ipv4Inet::new(Ipv4Addr::new(10, 126, 126, 0), 24).unwrap();
let mut current_dhcp_ip: Option<Ipv4Inet> = None;
@@ -286,26 +286,31 @@ impl Instance {
));
continue;
}
let mut new_nic_ctx = NicCtx::new(
global_ctx_c.clone(),
&peer_manager_c,
peer_packet_receiver.clone(),
);
if let Err(e) = new_nic_ctx.run(ip.address()).await {
tracing::error!(
?current_dhcp_ip,
?candidate_ipv4_addr,
?e,
"add ip failed"
#[cfg(not(target_os = "android"))]
{
let mut new_nic_ctx = NicCtx::new(
global_ctx_c.clone(),
&peer_manager_c,
_peer_packet_receiver.clone(),
);
global_ctx_c.set_ipv4(None);
continue;
if let Err(e) = new_nic_ctx.run(ip.address()).await {
tracing::error!(
?current_dhcp_ip,
?candidate_ipv4_addr,
?e,
"add ip failed"
);
global_ctx_c.set_ipv4(None);
continue;
}
Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx).await;
}
current_dhcp_ip = Some(ip);
global_ctx_c.set_ipv4(Some(ip.address()));
global_ctx_c
.issue_event(GlobalCtxEvent::DhcpIpv4Changed(last_ip, Some(ip.address())));
Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx).await;
} else {
current_dhcp_ip = None;
global_ctx_c.set_ipv4(None);
@@ -326,14 +331,17 @@ impl Instance {
if self.global_ctx.config.get_flags().no_tun {
self.peer_packet_receiver.lock().await.close();
} else if let Some(ipv4_addr) = self.global_ctx.get_ipv4() {
let mut new_nic_ctx = NicCtx::new(
self.global_ctx.clone(),
&self.peer_manager,
self.peer_packet_receiver.clone(),
);
new_nic_ctx.run(ipv4_addr).await?;
Self::use_new_nic_ctx(self.nic_ctx.clone(), new_nic_ctx).await;
} else {
#[cfg(not(target_os = "android"))]
if let Some(ipv4_addr) = self.global_ctx.get_ipv4() {
let mut new_nic_ctx = NicCtx::new(
self.global_ctx.clone(),
&self.peer_manager,
self.peer_packet_receiver.clone(),
);
new_nic_ctx.run(ipv4_addr).await?;
Self::use_new_nic_ctx(self.nic_ctx.clone(), new_nic_ctx).await;
}
}
if self.global_ctx.config.get_dhcp() {
@@ -506,4 +514,38 @@ impl Instance {
pub fn get_vpn_portal_inst(&self) -> Arc<Mutex<Box<dyn VpnPortal>>> {
self.vpn_portal.clone()
}
pub fn get_nic_ctx(&self) -> ArcNicCtx {
self.nic_ctx.clone()
}
pub fn get_peer_packet_receiver(&self) -> Arc<Mutex<PacketRecvChanReceiver>> {
self.peer_packet_receiver.clone()
}
#[cfg(target_os = "android")]
pub async fn setup_nic_ctx_for_android(
nic_ctx: ArcNicCtx,
global_ctx: ArcGlobalCtx,
peer_manager: Arc<PeerManager>,
peer_packet_receiver: Arc<Mutex<PacketRecvChanReceiver>>,
fd: i32,
) -> Result<(), anyhow::Error> {
println!("setup_nic_ctx_for_android, fd: {}", fd);
Self::clear_nic_ctx(nic_ctx.clone()).await;
if fd <= 0 {
return Ok(());
}
let mut new_nic_ctx = NicCtx::new(
global_ctx.clone(),
&peer_manager,
peer_packet_receiver.clone(),
);
new_nic_ctx
.run_for_android(fd)
.await
.with_context(|| "add ip failed")?;
Self::use_new_nic_ctx(nic_ctx.clone(), new_nic_ctx).await;
Ok(())
}
}

View File

@@ -274,7 +274,9 @@ impl VirtualNic {
#[cfg(target_os = "linux")]
{
let dev_name = self.global_ctx.get_flags().dev_name;
config.name(format!("{}{}", dev_name, 0));
if !dev_name.is_empty() {
config.name(format!("{}", dev_name));
}
config.platform(|config| {
// detect protocol by ourselves for cross platform
config.packet_information(false);
@@ -318,7 +320,37 @@ impl VirtualNic {
Ok(create_as_async(&config)?)
}
async fn create_dev_ret_err(&mut self) -> Result<Box<dyn Tunnel>, Error> {
#[cfg(target_os = "android")]
pub async fn create_dev_for_android(
&mut self,
tun_fd: std::os::fd::RawFd,
) -> Result<Box<dyn Tunnel>, Error> {
println!("tun_fd: {}", tun_fd);
let mut config = Configuration::default();
config.layer(Layer::L3);
config.raw_fd(tun_fd);
config.platform(|config| {
config.no_close_fd_on_drop(true);
});
config.up();
let dev = create_as_async(&config)?;
let (a, b) = BiLock::new(dev);
let ft = TunnelWrapper::new(
TunStream::new(a, false),
FramedWriter::new_with_converter(
TunAsyncWrite { l: b },
TunZCPacketToBytes::new(false),
),
None,
);
self.ifname = Some(format!("tunfd_{}", tun_fd));
Ok(Box::new(ft))
}
pub async fn create_dev(&mut self) -> Result<Box<dyn Tunnel>, Error> {
let dev = self.create_tun().await?;
let ifname = dev.get_ref().name()?;
self.ifcfg.wait_interface_show(ifname.as_str()).await?;
@@ -351,10 +383,6 @@ impl VirtualNic {
Ok(Box::new(ft))
}
pub async fn create_dev(&mut self) -> Result<Box<dyn Tunnel>, Error> {
self.create_dev_ret_err().await
}
pub fn ifname(&self) -> &str {
self.ifname.as_ref().unwrap().as_str()
}
@@ -589,6 +617,24 @@ impl NicCtx {
Ok(())
}
#[cfg(target_os = "android")]
pub async fn run_for_android(&mut self, tun_fd: std::os::fd::RawFd) -> Result<(), Error> {
let tunnel = {
let mut nic = self.nic.lock().await;
let ret = nic.create_dev_for_android(tun_fd).await?;
self.global_ctx
.issue_event(GlobalCtxEvent::TunDeviceReady(nic.ifname().to_string()));
ret
};
let (stream, sink) = tunnel.split();
self.do_forward_nic_to_peers(stream)?;
self.do_forward_peers_to_nic(sink);
Ok(())
}
}
#[cfg(test)]

View File

@@ -19,6 +19,7 @@ use crate::{
};
use chrono::{DateTime, Local};
use serde::{Deserialize, Serialize};
use tokio::task::JoinSet;
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct MyNodeInfo {
@@ -35,6 +36,7 @@ struct EasyTierData {
node_info: Arc<RwLock<MyNodeInfo>>,
routes: Arc<RwLock<Vec<Route>>>,
peers: Arc<RwLock<Vec<PeerInfo>>>,
tun_fd: Arc<RwLock<Option<i32>>>,
}
pub struct EasyTierLauncher {
@@ -69,6 +71,40 @@ impl EasyTierLauncher {
}
}
#[cfg(target_os = "android")]
async fn run_routine_for_android(
instance: &Instance,
data: &EasyTierData,
tasks: &mut JoinSet<()>,
) {
let global_ctx = instance.get_global_ctx();
let peer_mgr = instance.get_peer_manager();
let nic_ctx = instance.get_nic_ctx();
let peer_packet_receiver = instance.get_peer_packet_receiver();
let arc_tun_fd = data.tun_fd.clone();
tasks.spawn(async move {
let mut old_tun_fd = arc_tun_fd.read().unwrap().clone();
loop {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
let tun_fd = arc_tun_fd.read().unwrap().clone();
if tun_fd != old_tun_fd && tun_fd.is_some() {
let res = Instance::setup_nic_ctx_for_android(
nic_ctx.clone(),
global_ctx.clone(),
peer_mgr.clone(),
peer_packet_receiver.clone(),
tun_fd.unwrap(),
)
.await;
if res.is_ok() {
old_tun_fd = tun_fd;
}
}
}
});
}
async fn easytier_routine(
cfg: TomlConfigLoader,
stop_signal: Arc<tokio::sync::Notify>,
@@ -77,10 +113,12 @@ impl EasyTierLauncher {
let mut instance = Instance::new(cfg);
let peer_mgr = instance.get_peer_manager();
let mut tasks = JoinSet::new();
// Subscribe to global context events
let global_ctx = instance.get_global_ctx();
let data_c = data.clone();
tokio::spawn(async move {
tasks.spawn(async move {
let mut receiver = global_ctx.subscribe();
while let Ok(event) = receiver.recv().await {
Self::handle_easytier_event(event, data_c.clone()).await;
@@ -92,7 +130,7 @@ impl EasyTierLauncher {
let global_ctx_c = instance.get_global_ctx();
let peer_mgr_c = peer_mgr.clone();
let vpn_portal = instance.get_vpn_portal_inst();
tokio::spawn(async move {
tasks.spawn(async move {
loop {
let node_info = MyNodeInfo {
virtual_ipv4: global_ctx_c
@@ -123,8 +161,15 @@ impl EasyTierLauncher {
}
});
#[cfg(target_os = "android")]
Self::run_routine_for_android(&instance, &data, &mut tasks).await;
instance.run().await?;
stop_signal.notified().await;
tasks.abort_all();
drop(tasks);
Ok(())
}
@@ -266,6 +311,12 @@ impl NetworkInstance {
})
}
pub fn set_tun_fd(&mut self, tun_fd: i32) {
if let Some(launcher) = self.launcher.as_ref() {
launcher.data.tun_fd.write().unwrap().replace(tun_fd);
}
}
pub fn start(&mut self) -> Result<(), anyhow::Error> {
if self.is_easytier_running() {
return Ok(());

View File

@@ -1,5 +1,6 @@
use crate::common::PeerId;
#[cfg(target_os = "linux")]
mod three_node;
pub fn get_guest_veth_name(net_ns: &str) -> &str {