mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-04 01:55:41 +00:00
shared-tun: preserve member ownership on mobile
Introduce shared NIC source ownership and dispatcher handling so a single dev_name can be shared by multiple tun-enabled instances while keeping per-member IP and route claims distinct. Pass Android VpnService fd registration with per-instance source and route claims. Keep the VPN address list limited to real member addresses and allow AF_INET6 without installing hidden fd00::1. Invalidate dispatcher flow and NAT state when source ownership changes or a member unregisters. Avoid rewriting non-first IPv4 fragment payloads, and adjust fragmented TCP/UDP checksums without recomputing over partial fragment bodies. Preserve source-owner routing for equal-prefix route conflicts, keep ICMP echo NAT entries distinct by echo id, and retry stale flow-owner send failures from the original packet. Only record NAT state after a translated packet is accepted by its member. Apply Linux IPv4 route preferred-source hints for shared routes and keep route repair paths source-aware. Keep Darwin ifcfg access scoped to cleanup-only paths where netns is not available.
This commit is contained in:
@@ -46,12 +46,28 @@ impl IfConfiguerTrait for MacIfConfiger {
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
) -> Result<(), Error> {
|
||||
self.add_ipv4_route_with_source_hint(name, address, cidr_prefix, cost, None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn add_ipv4_route_with_source_hint(
|
||||
&self,
|
||||
name: &str,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
let source_hint = source_hint
|
||||
.map(|source| format!(" -ifa {}", source))
|
||||
.unwrap_or_default();
|
||||
run_shell_cmd(
|
||||
format!(
|
||||
"route -n add {} -netmask {} -interface {} -hopcount {}",
|
||||
"route -n add {} -netmask {} -interface {}{} -hopcount {}",
|
||||
address,
|
||||
cidr_to_subnet_mask(cidr_prefix),
|
||||
name,
|
||||
source_hint,
|
||||
cost.unwrap_or(7)
|
||||
)
|
||||
.as_str(),
|
||||
|
||||
@@ -31,6 +31,16 @@ pub trait IfConfiguerTrait: Send + Sync {
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn add_ipv4_route_with_source_hint(
|
||||
&self,
|
||||
name: &str,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
_source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
self.add_ipv4_route(name, address, cidr_prefix, cost).await
|
||||
}
|
||||
async fn remove_ipv4_route(
|
||||
&self,
|
||||
_name: &str,
|
||||
@@ -39,6 +49,16 @@ pub trait IfConfiguerTrait: Send + Sync {
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
async fn remove_ipv4_route_with_cost_and_source_hint(
|
||||
&self,
|
||||
name: &str,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
_cost: Option<i32>,
|
||||
_source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
self.remove_ipv4_route(name, address, cidr_prefix).await
|
||||
}
|
||||
async fn add_ipv4_ip(
|
||||
&self,
|
||||
_name: &str,
|
||||
|
||||
@@ -376,6 +376,64 @@ impl NetlinkIfConfiger {
|
||||
pub(crate) fn list_ipv6_route_messages() -> Result<Vec<RouteMessage>, Error> {
|
||||
Self::list_route_messages(AddressFamily::Inet6)
|
||||
}
|
||||
|
||||
fn ipv4_route_message(
|
||||
ifindex: u32,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> RouteMessage {
|
||||
let mut message = RouteMessage::default();
|
||||
|
||||
message.header.table = RouteHeader::RT_TABLE_MAIN;
|
||||
message.header.protocol = RouteProtocol::Static;
|
||||
message.header.scope = RouteScope::Universe;
|
||||
message.header.kind = RouteType::Unicast;
|
||||
message.header.address_family = AddressFamily::Inet;
|
||||
message.header.destination_prefix_length = cidr_prefix;
|
||||
|
||||
message
|
||||
.attributes
|
||||
.push(RouteAttribute::Priority(cost.unwrap_or(65535) as u32));
|
||||
message.attributes.push(RouteAttribute::Oif(ifindex));
|
||||
message
|
||||
.attributes
|
||||
.push(RouteAttribute::Destination(RouteAddress::Inet(address)));
|
||||
|
||||
if let Some(source_hint) = source_hint {
|
||||
message
|
||||
.attributes
|
||||
.push(RouteAttribute::PrefSource(RouteAddress::Inet(source_hint)));
|
||||
}
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
fn ipv4_route_target_matches(
|
||||
route: &Route,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
ifidx: u32,
|
||||
) -> bool {
|
||||
route.destination == IpAddr::V4(address)
|
||||
&& route.prefix == cidr_prefix
|
||||
&& route.ifindex == Some(ifidx)
|
||||
}
|
||||
|
||||
fn ipv4_route_exact_matches(
|
||||
route: &Route,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
ifidx: u32,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> bool {
|
||||
Self::ipv4_route_target_matches(route, address, cidr_prefix, ifidx)
|
||||
&& route.table == RouteHeader::RT_TABLE_MAIN
|
||||
&& route.metric == Some(cost.unwrap_or(65535) as u32)
|
||||
&& route.source_hint == source_hint.map(IpAddr::V4)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -387,29 +445,25 @@ impl IfConfiguerTrait for NetlinkIfConfiger {
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
) -> Result<(), Error> {
|
||||
let mut message = RouteMessage::default();
|
||||
|
||||
message.header.table = RouteHeader::RT_TABLE_MAIN;
|
||||
message.header.protocol = RouteProtocol::Static;
|
||||
message.header.scope = RouteScope::Universe;
|
||||
message.header.kind = RouteType::Unicast;
|
||||
message.header.address_family = AddressFamily::Inet;
|
||||
// metric
|
||||
message
|
||||
.attributes
|
||||
.push(RouteAttribute::Priority(cost.unwrap_or(65535) as u32));
|
||||
// output interface
|
||||
message
|
||||
.attributes
|
||||
.push(RouteAttribute::Oif(NetlinkIfConfiger::get_interface_index(
|
||||
name,
|
||||
)?));
|
||||
// source address
|
||||
message.header.destination_prefix_length = cidr_prefix;
|
||||
message
|
||||
.attributes
|
||||
.push(RouteAttribute::Destination(RouteAddress::Inet(address)));
|
||||
self.add_ipv4_route_with_source_hint(name, address, cidr_prefix, cost, None)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn add_ipv4_route_with_source_hint(
|
||||
&self,
|
||||
name: &str,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
let message = NetlinkIfConfiger::ipv4_route_message(
|
||||
NetlinkIfConfiger::get_interface_index(name)?,
|
||||
address,
|
||||
cidr_prefix,
|
||||
cost,
|
||||
source_hint,
|
||||
);
|
||||
send_netlink_req_and_wait_one_resp(RouteNetlinkMessage::NewRoute(message), false)
|
||||
}
|
||||
|
||||
@@ -424,10 +478,41 @@ impl IfConfiguerTrait for NetlinkIfConfiger {
|
||||
|
||||
for msg in routes {
|
||||
let other_route: Route = msg.clone().into();
|
||||
if other_route.destination == std::net::IpAddr::V4(address)
|
||||
&& other_route.prefix == cidr_prefix
|
||||
&& other_route.ifindex == Some(ifidx)
|
||||
{
|
||||
if NetlinkIfConfiger::ipv4_route_target_matches(
|
||||
&other_route,
|
||||
address,
|
||||
cidr_prefix,
|
||||
ifidx,
|
||||
) {
|
||||
send_netlink_req_and_wait_one_resp(RouteNetlinkMessage::DelRoute(msg), true)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_ipv4_route_with_cost_and_source_hint(
|
||||
&self,
|
||||
name: &str,
|
||||
address: Ipv4Addr,
|
||||
cidr_prefix: u8,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
let routes = Self::list_routes()?;
|
||||
let ifidx = NetlinkIfConfiger::get_interface_index(name)?;
|
||||
|
||||
for msg in routes {
|
||||
let other_route: Route = msg.clone().into();
|
||||
if NetlinkIfConfiger::ipv4_route_exact_matches(
|
||||
&other_route,
|
||||
address,
|
||||
cidr_prefix,
|
||||
ifidx,
|
||||
cost,
|
||||
source_hint,
|
||||
) {
|
||||
send_netlink_req_and_wait_one_resp(RouteNetlinkMessage::DelRoute(msg), true)?;
|
||||
return Ok(());
|
||||
}
|
||||
@@ -666,6 +751,89 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv4_route_message_includes_pref_source_when_source_hint_is_set() {
|
||||
let source_hint = Ipv4Addr::new(10, 231, 1, 1);
|
||||
let message = NetlinkIfConfiger::ipv4_route_message(
|
||||
7,
|
||||
Ipv4Addr::new(10, 99, 0, 0),
|
||||
24,
|
||||
Some(123),
|
||||
Some(source_hint),
|
||||
);
|
||||
|
||||
assert_eq!(message.header.destination_prefix_length, 24);
|
||||
assert!(message.attributes.iter().any(|attr| {
|
||||
matches!(
|
||||
attr,
|
||||
RouteAttribute::PrefSource(RouteAddress::Inet(source)) if *source == source_hint
|
||||
)
|
||||
}));
|
||||
assert!(message.attributes.iter().any(|attr| {
|
||||
matches!(attr, RouteAttribute::Priority(priority) if *priority == 123)
|
||||
}));
|
||||
assert!(
|
||||
message
|
||||
.attributes
|
||||
.iter()
|
||||
.any(|attr| matches!(attr, RouteAttribute::Oif(7)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv4_route_exact_match_distinguishes_metric_and_pref_source() {
|
||||
let address = Ipv4Addr::new(10, 99, 0, 0);
|
||||
let source_hint = Ipv4Addr::new(10, 99, 0, 1);
|
||||
let other_source_hint = Ipv4Addr::new(10, 99, 0, 2);
|
||||
let route: Route =
|
||||
NetlinkIfConfiger::ipv4_route_message(7, address, 24, Some(123), Some(source_hint))
|
||||
.into();
|
||||
|
||||
assert!(NetlinkIfConfiger::ipv4_route_exact_matches(
|
||||
&route,
|
||||
address,
|
||||
24,
|
||||
7,
|
||||
Some(123),
|
||||
Some(source_hint),
|
||||
));
|
||||
assert!(!NetlinkIfConfiger::ipv4_route_exact_matches(
|
||||
&route,
|
||||
address,
|
||||
24,
|
||||
7,
|
||||
Some(124),
|
||||
Some(source_hint),
|
||||
));
|
||||
assert!(!NetlinkIfConfiger::ipv4_route_exact_matches(
|
||||
&route,
|
||||
address,
|
||||
24,
|
||||
7,
|
||||
Some(123),
|
||||
Some(other_source_hint),
|
||||
));
|
||||
assert!(!NetlinkIfConfiger::ipv4_route_exact_matches(
|
||||
&route,
|
||||
address,
|
||||
24,
|
||||
7,
|
||||
Some(123),
|
||||
None,
|
||||
));
|
||||
|
||||
let mut non_main_table_route = route.clone();
|
||||
non_main_table_route.table = 100;
|
||||
assert!(!NetlinkIfConfiger::ipv4_route_exact_matches(
|
||||
&non_main_table_route,
|
||||
address,
|
||||
24,
|
||||
7,
|
||||
Some(123),
|
||||
Some(source_hint),
|
||||
));
|
||||
}
|
||||
|
||||
struct PrepareEnv {}
|
||||
impl PrepareEnv {
|
||||
fn new() -> Self {
|
||||
|
||||
@@ -193,6 +193,8 @@ impl IpProxy {
|
||||
|
||||
#[cfg(feature = "tun")]
|
||||
type NicCtx = super::virtual_nic::NicCtx;
|
||||
#[cfg(all(feature = "tun", mobile))]
|
||||
use super::virtual_nic::MobileTunSources;
|
||||
|
||||
#[cfg(feature = "magic-dns")]
|
||||
struct MagicDnsContainer {
|
||||
@@ -910,7 +912,8 @@ impl Instance {
|
||||
close_notifier: Arc<Notify>,
|
||||
shared_virtual_nic_registry: ArcSharedVirtualNicRegistry,
|
||||
) -> Result<NicCtx, Error> {
|
||||
if global_ctx.get_flags().dev_name.is_empty() {
|
||||
let flags = global_ctx.get_flags();
|
||||
if flags.dev_name.is_empty() {
|
||||
return Ok(NicCtx::new(
|
||||
global_ctx,
|
||||
peer_manager,
|
||||
@@ -1730,6 +1733,7 @@ impl Instance {
|
||||
peer_packet_receiver: Arc<Mutex<PacketRecvChanReceiver>>,
|
||||
shared_virtual_nic_registry: ArcSharedVirtualNicRegistry,
|
||||
fd: i32,
|
||||
sources: MobileTunSources,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
tracing::info!("setup_nic_ctx_for_mobile, fd: {}", fd);
|
||||
Self::clear_nic_ctx(nic_ctx.clone(), peer_packet_receiver.clone()).await;
|
||||
@@ -1747,7 +1751,7 @@ impl Instance {
|
||||
.await
|
||||
.with_context(|| "create nic ctx failed")?;
|
||||
new_nic_ctx
|
||||
.run_for_mobile(fd)
|
||||
.run_for_mobile(fd, sources)
|
||||
.await
|
||||
.with_context(|| "add ip failed")?;
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@ pub struct SharedIfConfigDelta {
|
||||
pub ipv4_addresses: OwnedItemDelta<Ipv4Inet>,
|
||||
pub ipv6_addresses: OwnedItemDelta<Ipv6Inet>,
|
||||
pub ipv4_routes: OwnedItemDelta<SharedIpv4Route>,
|
||||
pub ipv4_route_removed_old_source_hints: BTreeMap<SharedIpv4Route, Option<Ipv4Addr>>,
|
||||
pub ipv4_route_source_changed: BTreeSet<SharedIpv4Route>,
|
||||
pub ipv4_route_source_changed_old_hints: BTreeMap<SharedIpv4Route, Option<Ipv4Addr>>,
|
||||
pub ipv6_routes: OwnedItemDelta<SharedIpv6Route>,
|
||||
pub mtu: Option<SharedMtuChange>,
|
||||
}
|
||||
@@ -131,6 +134,12 @@ impl SharedIfConfig {
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let old_mtu = self.effective_mtu();
|
||||
let source_change_candidates = old_claims
|
||||
.ipv4_routes
|
||||
.union(&claims.ipv4_routes)
|
||||
.cloned()
|
||||
.collect::<BTreeSet<_>>();
|
||||
let old_ipv4_route_sources = self.ipv4_route_sources(&source_change_candidates);
|
||||
|
||||
let ipv4_addresses = update_owned_items(
|
||||
&mut self.ipv4_address_owners,
|
||||
@@ -159,11 +168,22 @@ impl SharedIfConfig {
|
||||
|
||||
update_member_mtu(&mut self.member_mtu, member_id, claims.mtu);
|
||||
self.member_claims.insert(member_id, claims);
|
||||
let ipv4_route_removed_old_source_hints =
|
||||
old_ipv4_route_hints(&old_ipv4_route_sources, &ipv4_routes.removed);
|
||||
let ipv4_route_source_changed_old_hints =
|
||||
self.changed_ipv4_route_sources(&old_ipv4_route_sources, &ipv4_routes);
|
||||
let ipv4_route_source_changed = ipv4_route_source_changed_old_hints
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
SharedIfConfigDelta {
|
||||
ipv4_addresses,
|
||||
ipv6_addresses,
|
||||
ipv4_routes,
|
||||
ipv4_route_removed_old_source_hints,
|
||||
ipv4_route_source_changed,
|
||||
ipv4_route_source_changed_old_hints,
|
||||
ipv6_routes,
|
||||
mtu: mtu_delta(old_mtu, self.effective_mtu()),
|
||||
}
|
||||
@@ -173,8 +193,10 @@ impl SharedIfConfig {
|
||||
&mut self,
|
||||
member_id: SharedVirtualNicMemberId,
|
||||
) -> Option<SharedIfConfigDelta> {
|
||||
let old_claims = self.member_claims.remove(&member_id)?;
|
||||
let old_claims = self.member_claims.get(&member_id).cloned()?;
|
||||
let old_mtu = self.effective_mtu();
|
||||
let old_ipv4_route_sources = self.ipv4_route_sources(&old_claims.ipv4_routes);
|
||||
self.member_claims.remove(&member_id);
|
||||
|
||||
let ipv4_addresses = remove_owned_items(
|
||||
&mut self.ipv4_address_owners,
|
||||
@@ -198,11 +220,22 @@ impl SharedIfConfig {
|
||||
);
|
||||
|
||||
self.member_mtu.remove(&member_id);
|
||||
let ipv4_route_removed_old_source_hints =
|
||||
old_ipv4_route_hints(&old_ipv4_route_sources, &ipv4_routes.removed);
|
||||
let ipv4_route_source_changed_old_hints =
|
||||
self.changed_ipv4_route_sources(&old_ipv4_route_sources, &ipv4_routes);
|
||||
let ipv4_route_source_changed = ipv4_route_source_changed_old_hints
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
Some(SharedIfConfigDelta {
|
||||
ipv4_addresses,
|
||||
ipv6_addresses,
|
||||
ipv4_routes,
|
||||
ipv4_route_removed_old_source_hints,
|
||||
ipv4_route_source_changed,
|
||||
ipv4_route_source_changed_old_hints,
|
||||
ipv6_routes,
|
||||
mtu: mtu_delta(old_mtu, self.effective_mtu()),
|
||||
})
|
||||
@@ -250,6 +283,57 @@ impl SharedIfConfig {
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn ipv4_route_source_hint(&self, route: &SharedIpv4Route) -> Option<Ipv4Addr> {
|
||||
let owners = self.ipv4_route_owners.get(route)?;
|
||||
let route_inet = Ipv4Inet::new(route.address, route.prefix).ok();
|
||||
let mut fallback = None;
|
||||
|
||||
for owner in owners {
|
||||
let Some(claims) = self.member_claims.get(owner) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for address in &claims.ipv4_addresses {
|
||||
fallback.get_or_insert(address.address());
|
||||
if route_inet
|
||||
.as_ref()
|
||||
.is_some_and(|route_inet| route_inet.contains(&address.address()))
|
||||
{
|
||||
return Some(address.address());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fallback
|
||||
}
|
||||
|
||||
fn ipv4_route_sources(
|
||||
&self,
|
||||
routes: &BTreeSet<SharedIpv4Route>,
|
||||
) -> BTreeMap<SharedIpv4Route, Option<Ipv4Addr>> {
|
||||
routes
|
||||
.iter()
|
||||
.map(|route| (route.clone(), self.ipv4_route_source_hint(route)))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn changed_ipv4_route_sources(
|
||||
&self,
|
||||
old_sources: &BTreeMap<SharedIpv4Route, Option<Ipv4Addr>>,
|
||||
route_delta: &OwnedItemDelta<SharedIpv4Route>,
|
||||
) -> BTreeMap<SharedIpv4Route, Option<Ipv4Addr>> {
|
||||
old_sources
|
||||
.iter()
|
||||
.filter(|(route, old_source)| {
|
||||
!route_delta.added.contains(*route)
|
||||
&& !route_delta.removed.contains(*route)
|
||||
&& self.ipv4_route_owners.contains_key(*route)
|
||||
&& self.ipv4_route_source_hint(route) != **old_source
|
||||
})
|
||||
.map(|(route, old_source)| (route.clone(), *old_source))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SharedVirtualNic {
|
||||
@@ -509,7 +593,24 @@ impl SharedVirtualNic {
|
||||
let nic = self.nic.lock().await;
|
||||
|
||||
for route in &delta.ipv4_routes.removed {
|
||||
ignore_removed_ifcfg_not_found(nic.remove_route(route.address, route.prefix).await)?;
|
||||
let source_hint = delta
|
||||
.ipv4_route_removed_old_source_hints
|
||||
.get(route)
|
||||
.copied()
|
||||
.flatten();
|
||||
ignore_removed_ifcfg_not_found(
|
||||
remove_shared_ipv4_route(&nic, route, source_hint).await,
|
||||
)?;
|
||||
}
|
||||
for route in &delta.ipv4_route_source_changed {
|
||||
let source_hint = delta
|
||||
.ipv4_route_source_changed_old_hints
|
||||
.get(route)
|
||||
.copied()
|
||||
.flatten();
|
||||
ignore_removed_ifcfg_not_found(
|
||||
remove_shared_ipv4_route(&nic, route, source_hint).await,
|
||||
)?;
|
||||
}
|
||||
for route in &delta.ipv6_routes.removed {
|
||||
ignore_removed_ifcfg_not_found(
|
||||
@@ -531,8 +632,10 @@ impl SharedVirtualNic {
|
||||
.await?;
|
||||
}
|
||||
for route in &delta.ipv4_routes.added {
|
||||
nic.add_route_with_cost(route.address, route.prefix, route.cost)
|
||||
.await?;
|
||||
add_shared_ipv4_route(&nic, route, _next_ifcfg).await?;
|
||||
}
|
||||
for route in &delta.ipv4_route_source_changed {
|
||||
add_shared_ipv4_route(&nic, route, _next_ifcfg).await?;
|
||||
}
|
||||
for route in &delta.ipv6_routes.added {
|
||||
nic.add_ipv6_route_with_cost(route.address, route.prefix, route.cost)
|
||||
@@ -548,8 +651,7 @@ impl SharedVirtualNic {
|
||||
if !delta.ipv4_addresses.removed.is_empty() {
|
||||
for route in _next_ifcfg.ipv4_route_owners.keys() {
|
||||
ignore_added_ifcfg_already_exists(
|
||||
nic.add_route_with_cost(route.address, route.prefix, route.cost)
|
||||
.await,
|
||||
add_shared_ipv4_route(&nic, route, _next_ifcfg).await,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@@ -631,9 +733,7 @@ impl SharedVirtualNic {
|
||||
dispatcher: &SharedVirtualNicDispatcher,
|
||||
) -> Result<(), Error> {
|
||||
for (member_id, claims) in &self.ifcfg.member_claims {
|
||||
dispatcher
|
||||
.update_sources(*member_id, &claims.ipv4_addresses, &claims.ipv6_addresses)
|
||||
.await?;
|
||||
dispatcher.update_sources(*member_id, claims).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -644,17 +744,9 @@ impl SharedVirtualNic {
|
||||
old_claims: &SharedIfConfigClaims,
|
||||
next_claims: &SharedIfConfigClaims,
|
||||
) -> Result<(), Error> {
|
||||
let mut active_ipv4_addresses = old_claims.ipv4_addresses.clone();
|
||||
active_ipv4_addresses.extend(next_claims.ipv4_addresses.iter().copied());
|
||||
let mut active_ipv6_addresses = old_claims.ipv6_addresses.clone();
|
||||
active_ipv6_addresses.extend(next_claims.ipv6_addresses.iter().copied());
|
||||
|
||||
self.sync_dispatcher_sources_for_addresses(
|
||||
member_id,
|
||||
&active_ipv4_addresses,
|
||||
&active_ipv6_addresses,
|
||||
)
|
||||
.await
|
||||
let active_claims = dispatcher_claims_for_ifcfg_transition(old_claims, next_claims);
|
||||
self.sync_dispatcher_sources_for_claims(member_id, &active_claims)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn sync_dispatcher_sources_for_member(
|
||||
@@ -662,24 +754,17 @@ impl SharedVirtualNic {
|
||||
member_id: SharedVirtualNicMemberId,
|
||||
claims: &SharedIfConfigClaims,
|
||||
) -> Result<(), Error> {
|
||||
self.sync_dispatcher_sources_for_addresses(
|
||||
member_id,
|
||||
&claims.ipv4_addresses,
|
||||
&claims.ipv6_addresses,
|
||||
)
|
||||
.await
|
||||
self.sync_dispatcher_sources_for_claims(member_id, claims)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn sync_dispatcher_sources_for_addresses(
|
||||
async fn sync_dispatcher_sources_for_claims(
|
||||
&self,
|
||||
member_id: SharedVirtualNicMemberId,
|
||||
ipv4_addresses: &BTreeSet<Ipv4Inet>,
|
||||
ipv6_addresses: &BTreeSet<Ipv6Inet>,
|
||||
claims: &SharedIfConfigClaims,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(dispatcher) = &self.dispatcher {
|
||||
dispatcher
|
||||
.update_sources(member_id, ipv4_addresses, ipv6_addresses)
|
||||
.await?;
|
||||
dispatcher.update_sources(member_id, claims).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -695,6 +780,75 @@ impl SharedVirtualNic {
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatcher_claims_for_ifcfg_transition(
|
||||
old_claims: &SharedIfConfigClaims,
|
||||
next_claims: &SharedIfConfigClaims,
|
||||
) -> SharedIfConfigClaims {
|
||||
let mut claims = SharedIfConfigClaims::default();
|
||||
claims
|
||||
.ipv4_addresses
|
||||
.extend(old_claims.ipv4_addresses.iter().copied());
|
||||
claims
|
||||
.ipv4_addresses
|
||||
.extend(next_claims.ipv4_addresses.iter().copied());
|
||||
claims
|
||||
.ipv6_addresses
|
||||
.extend(old_claims.ipv6_addresses.iter().copied());
|
||||
claims
|
||||
.ipv6_addresses
|
||||
.extend(next_claims.ipv6_addresses.iter().copied());
|
||||
claims
|
||||
.ipv4_routes
|
||||
.extend(old_claims.ipv4_routes.iter().cloned());
|
||||
claims
|
||||
.ipv4_routes
|
||||
.extend(next_claims.ipv4_routes.iter().cloned());
|
||||
claims
|
||||
.ipv6_routes
|
||||
.extend(old_claims.ipv6_routes.iter().cloned());
|
||||
claims
|
||||
.ipv6_routes
|
||||
.extend(next_claims.ipv6_routes.iter().cloned());
|
||||
claims
|
||||
}
|
||||
|
||||
async fn add_shared_ipv4_route(
|
||||
nic: &VirtualNic,
|
||||
route: &SharedIpv4Route,
|
||||
ifcfg: &SharedIfConfig,
|
||||
) -> Result<(), Error> {
|
||||
nic.add_route_with_cost_and_source_hint(
|
||||
route.address,
|
||||
route.prefix,
|
||||
route.cost,
|
||||
ifcfg.ipv4_route_source_hint(route),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn remove_shared_ipv4_route(
|
||||
nic: &VirtualNic,
|
||||
route: &SharedIpv4Route,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
nic.remove_route_with_cost_and_source_hint(route.address, route.prefix, route.cost, source_hint)
|
||||
.await
|
||||
}
|
||||
|
||||
fn old_ipv4_route_hints(
|
||||
old_sources: &BTreeMap<SharedIpv4Route, Option<Ipv4Addr>>,
|
||||
routes: &BTreeSet<SharedIpv4Route>,
|
||||
) -> BTreeMap<SharedIpv4Route, Option<Ipv4Addr>> {
|
||||
routes
|
||||
.iter()
|
||||
.filter_map(|route| {
|
||||
old_sources
|
||||
.get(route)
|
||||
.map(|source_hint| (route.clone(), *source_hint))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn ignore_removed_ifcfg_not_found(result: Result<(), Error>) -> Result<(), Error> {
|
||||
match result {
|
||||
Err(Error::NotFound) => Ok(()),
|
||||
@@ -897,8 +1051,7 @@ impl SharedVirtualNicMember {
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ip(&self, ip: Ipv4Addr, cidr: i32) -> Result<(), Error> {
|
||||
let ip = ipv4_inet(ip, cidr)?;
|
||||
pub async fn add_mobile_source_ip(&self, ip: Ipv4Inet) -> Result<(), Error> {
|
||||
self.update_claims_for_mobile(|claims| {
|
||||
claims.ipv4_addresses.insert(ip);
|
||||
})
|
||||
@@ -906,14 +1059,29 @@ impl SharedVirtualNicMember {
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ipv6(&self, ip: Ipv6Addr, cidr: i32) -> Result<(), Error> {
|
||||
let ip = ipv6_inet(ip, cidr)?;
|
||||
pub async fn add_mobile_source_ipv6(&self, ip: Ipv6Inet) -> Result<(), Error> {
|
||||
self.update_claims_for_mobile(|claims| {
|
||||
claims.ipv6_addresses.insert(ip);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ipv4_route(&self, route: SharedIpv4Route) -> Result<(), Error> {
|
||||
self.update_claims_for_mobile(|claims| {
|
||||
claims.ipv4_routes.insert(route);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ipv6_route(&self, route: SharedIpv6Route) -> Result<(), Error> {
|
||||
self.update_claims_for_mobile(|claims| {
|
||||
claims.ipv6_routes.insert(route);
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove_ipv6(&self, ip: Option<Ipv6Inet>) -> Result<(), Error> {
|
||||
self.update_claims(|claims| match ip {
|
||||
Some(ip) => {
|
||||
@@ -1292,6 +1460,17 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn claims_with_ipv4_address_and_route(
|
||||
address: Ipv4Inet,
|
||||
route: SharedIpv4Route,
|
||||
) -> SharedIfConfigClaims {
|
||||
SharedIfConfigClaims {
|
||||
ipv4_addresses: BTreeSet::from([address]),
|
||||
ipv4_routes: BTreeSet::from([route]),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn virtual_nic_config() -> VirtualNicConfig {
|
||||
VirtualNicConfig::new(String::new(), 1500, NetNS::new(None))
|
||||
}
|
||||
@@ -1385,6 +1564,140 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ipv4_route_source_hint_prefers_address_inside_route() {
|
||||
let route = SharedIpv4Route::new(Ipv4Addr::new(10, 90, 1, 0), 24, None);
|
||||
let member = member_id(1);
|
||||
let mut ifcfg = SharedIfConfig::default();
|
||||
|
||||
ifcfg.apply_member_claims(
|
||||
member,
|
||||
SharedIfConfigClaims {
|
||||
ipv4_addresses: BTreeSet::from([
|
||||
Ipv4Inet::from_str("10.1.1.1/24").unwrap(),
|
||||
Ipv4Inet::from_str("10.90.1.1/24").unwrap(),
|
||||
]),
|
||||
ipv4_routes: BTreeSet::from([route.clone()]),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
ifcfg.ipv4_route_source_hint(&route),
|
||||
Some(Ipv4Addr::new(10, 90, 1, 1))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn adding_better_ipv4_route_owner_marks_source_change() {
|
||||
let route = SharedIpv4Route::new(Ipv4Addr::new(10, 90, 2, 0), 24, None);
|
||||
let first = member_id(1);
|
||||
let second = member_id(2);
|
||||
let mut ifcfg = SharedIfConfig::default();
|
||||
ifcfg.apply_member_claims(
|
||||
first,
|
||||
claims_with_ipv4_address_and_route(
|
||||
Ipv4Inet::from_str("10.1.2.1/24").unwrap(),
|
||||
route.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
let delta = ifcfg.apply_member_claims(
|
||||
second,
|
||||
claims_with_ipv4_address_and_route(
|
||||
Ipv4Inet::from_str("10.90.2.1/24").unwrap(),
|
||||
route.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
assert!(delta.ipv4_routes.added.is_empty());
|
||||
assert_eq!(
|
||||
delta.ipv4_route_source_changed,
|
||||
BTreeSet::from([route.clone()])
|
||||
);
|
||||
assert_eq!(
|
||||
delta.ipv4_route_source_changed_old_hints,
|
||||
BTreeMap::from([(route.clone(), Some(Ipv4Addr::new(10, 1, 2, 1)))])
|
||||
);
|
||||
assert_eq!(
|
||||
ifcfg.ipv4_route_source_hint(&route),
|
||||
Some(Ipv4Addr::new(10, 90, 2, 1))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removing_ipv4_route_owner_marks_source_change_when_route_remains() {
|
||||
let route = SharedIpv4Route::new(Ipv4Addr::new(10, 90, 3, 0), 24, None);
|
||||
let first = member_id(1);
|
||||
let second = member_id(2);
|
||||
let mut ifcfg = SharedIfConfig::default();
|
||||
ifcfg.apply_member_claims(
|
||||
first,
|
||||
claims_with_ipv4_address_and_route(
|
||||
Ipv4Inet::from_str("10.1.3.1/24").unwrap(),
|
||||
route.clone(),
|
||||
),
|
||||
);
|
||||
ifcfg.apply_member_claims(
|
||||
second,
|
||||
claims_with_ipv4_address_and_route(
|
||||
Ipv4Inet::from_str("10.90.3.1/24").unwrap(),
|
||||
route.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
let delta = ifcfg.remove_member(second).unwrap();
|
||||
|
||||
assert!(delta.ipv4_routes.removed.is_empty());
|
||||
assert_eq!(
|
||||
delta.ipv4_route_source_changed,
|
||||
BTreeSet::from([route.clone()])
|
||||
);
|
||||
assert_eq!(
|
||||
delta.ipv4_route_source_changed_old_hints,
|
||||
BTreeMap::from([(route.clone(), Some(Ipv4Addr::new(10, 90, 3, 1)))])
|
||||
);
|
||||
assert_eq!(
|
||||
ifcfg.ipv4_route_source_hint(&route),
|
||||
Some(Ipv4Addr::new(10, 1, 3, 1))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn removing_ipv4_route_records_old_source_hint_per_cost() {
|
||||
let kept_route = SharedIpv4Route::new(Ipv4Addr::new(10, 90, 4, 0), 24, Some(10));
|
||||
let removed_route = SharedIpv4Route::new(Ipv4Addr::new(10, 90, 4, 0), 24, Some(20));
|
||||
let member = member_id(1);
|
||||
let address = Ipv4Inet::from_str("10.90.4.1/24").unwrap();
|
||||
let mut ifcfg = SharedIfConfig::default();
|
||||
ifcfg.apply_member_claims(
|
||||
member,
|
||||
SharedIfConfigClaims {
|
||||
ipv4_addresses: BTreeSet::from([address]),
|
||||
ipv4_routes: BTreeSet::from([kept_route.clone(), removed_route.clone()]),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let delta = ifcfg.apply_member_claims(
|
||||
member,
|
||||
SharedIfConfigClaims {
|
||||
ipv4_addresses: BTreeSet::from([address]),
|
||||
ipv4_routes: BTreeSet::from([kept_route.clone()]),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
delta.ipv4_routes.removed,
|
||||
BTreeSet::from([removed_route.clone()])
|
||||
);
|
||||
assert_eq!(
|
||||
delta.ipv4_route_removed_old_source_hints,
|
||||
BTreeMap::from([(removed_route, Some(Ipv4Addr::new(10, 90, 4, 1)))])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shared_virtual_nic_wraps_virtual_nic_and_tracks_ifcfg() {
|
||||
let mut shared_nic = SharedVirtualNic::new(virtual_nic_config());
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,10 +46,82 @@ use crate::common::ifcfg::RegistryManager;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::shared_virtual_nic::SharedVirtualNic;
|
||||
#[cfg(mobile)]
|
||||
use super::shared_virtual_nic::{SharedIpv4Route, SharedIpv6Route};
|
||||
use super::shared_virtual_nic::{
|
||||
SharedVirtualNicMember, SharedVirtualNicMemberId, SharedVirtualNicRegistry,
|
||||
};
|
||||
|
||||
#[cfg(mobile)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MobileTunSources {
|
||||
pub ipv4: Vec<Ipv4Inet>,
|
||||
pub ipv6: Vec<Ipv6Inet>,
|
||||
pub ipv4_routes: Vec<SharedIpv4Route>,
|
||||
pub ipv6_routes: Vec<SharedIpv6Route>,
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
impl MobileTunSources {
|
||||
pub fn parse(
|
||||
ipv4: Vec<String>,
|
||||
ipv6: Vec<String>,
|
||||
ipv4_routes: Vec<String>,
|
||||
ipv6_routes: Vec<String>,
|
||||
) -> Result<Self, Error> {
|
||||
let ipv4 = ipv4
|
||||
.into_iter()
|
||||
.map(|addr| {
|
||||
addr.parse::<Ipv4Inet>()
|
||||
.map_err(|err| anyhow::anyhow!("invalid IPv4 source {addr}: {err}").into())
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
let ipv6 = ipv6
|
||||
.into_iter()
|
||||
.map(|addr| {
|
||||
addr.parse::<Ipv6Inet>()
|
||||
.map_err(|err| anyhow::anyhow!("invalid IPv6 source {addr}: {err}").into())
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
let ipv4_routes = ipv4_routes
|
||||
.into_iter()
|
||||
.map(|route| {
|
||||
let route = route.parse::<Ipv4Inet>().map_err(|err| {
|
||||
let err: Error =
|
||||
anyhow::anyhow!("invalid IPv4 route source {route}: {err}").into();
|
||||
err
|
||||
})?;
|
||||
Ok(SharedIpv4Route::new(
|
||||
route.address(),
|
||||
route.network_length(),
|
||||
None,
|
||||
))
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
let ipv6_routes = ipv6_routes
|
||||
.into_iter()
|
||||
.map(|route| {
|
||||
let route = route.parse::<Ipv6Inet>().map_err(|err| {
|
||||
let err: Error =
|
||||
anyhow::anyhow!("invalid IPv6 route source {route}: {err}").into();
|
||||
err
|
||||
})?;
|
||||
Ok(SharedIpv6Route::new(
|
||||
route.address(),
|
||||
route.network_length(),
|
||||
None,
|
||||
))
|
||||
})
|
||||
.collect::<Result<Vec<_>, Error>>()?;
|
||||
Ok(Self {
|
||||
ipv4,
|
||||
ipv6,
|
||||
ipv4_routes,
|
||||
ipv6_routes,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
pub struct TunStream {
|
||||
#[pin]
|
||||
@@ -765,10 +837,21 @@ impl VirtualNic {
|
||||
address: Ipv4Addr,
|
||||
cidr: u8,
|
||||
cost: Option<i32>,
|
||||
) -> Result<(), Error> {
|
||||
self.add_route_with_cost_and_source_hint(address, cidr, cost, None)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn add_route_with_cost_and_source_hint(
|
||||
&self,
|
||||
address: Ipv4Addr,
|
||||
cidr: u8,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
let _g = self.config.net_ns.guard();
|
||||
self.ifcfg
|
||||
.add_ipv4_route(self.ifname(), address, cidr, cost)
|
||||
.add_ipv4_route_with_source_hint(self.ifname(), address, cidr, cost, source_hint)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -781,6 +864,26 @@ impl VirtualNic {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove_route_with_cost_and_source_hint(
|
||||
&self,
|
||||
address: Ipv4Addr,
|
||||
cidr: u8,
|
||||
cost: Option<i32>,
|
||||
source_hint: Option<Ipv4Addr>,
|
||||
) -> Result<(), Error> {
|
||||
let _g = self.config.net_ns.guard();
|
||||
self.ifcfg
|
||||
.remove_ipv4_route_with_cost_and_source_hint(
|
||||
self.ifname(),
|
||||
address,
|
||||
cidr,
|
||||
cost,
|
||||
source_hint,
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_ipv6_route(&self, address: Ipv6Addr, cidr: u8) -> Result<(), Error> {
|
||||
self.add_ipv6_route_with_cost(address, cidr, None).await
|
||||
}
|
||||
@@ -1031,18 +1134,34 @@ impl NicBackend {
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ip(&self, ip: Ipv4Addr, cidr: i32) -> Result<(), Error> {
|
||||
pub async fn add_mobile_source_ip(&self, ip: Ipv4Inet) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Dedicated(_) => Ok(()),
|
||||
Self::Shared(member) => member.add_mobile_source_ip(ip, cidr).await,
|
||||
Self::Shared(member) => member.add_mobile_source_ip(ip).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ipv6(&self, ip: Ipv6Addr, cidr: i32) -> Result<(), Error> {
|
||||
pub async fn add_mobile_source_ipv6(&self, ip: Ipv6Inet) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Dedicated(_) => Ok(()),
|
||||
Self::Shared(member) => member.add_mobile_source_ipv6(ip, cidr).await,
|
||||
Self::Shared(member) => member.add_mobile_source_ipv6(ip).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ipv4_route(&self, route: SharedIpv4Route) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Dedicated(_) => Ok(()),
|
||||
Self::Shared(member) => member.add_mobile_source_ipv4_route(route).await,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn add_mobile_source_ipv6_route(&self, route: SharedIpv6Route) -> Result<(), Error> {
|
||||
match self {
|
||||
Self::Dedicated(_) => Ok(()),
|
||||
Self::Shared(member) => member.add_mobile_source_ipv6_route(route).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1739,7 +1858,11 @@ impl NicCtx {
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub async fn run_for_mobile(&mut self, tun_fd: std::os::fd::RawFd) -> Result<(), Error> {
|
||||
pub async fn run_for_mobile(
|
||||
&mut self,
|
||||
tun_fd: std::os::fd::RawFd,
|
||||
sources: MobileTunSources,
|
||||
) -> Result<(), Error> {
|
||||
let (tunnel, ifname) = match self.backend.create_dev_for_mobile(tun_fd).await {
|
||||
Ok(ret) => {
|
||||
let ifname = self
|
||||
@@ -1756,14 +1879,20 @@ impl NicCtx {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(ipv4_addr) = self.global_ctx.get_ipv4() {
|
||||
for ipv4_addr in sources.ipv4 {
|
||||
self.backend.add_mobile_source_ip(ipv4_addr).await?;
|
||||
}
|
||||
for ipv6_addr in sources.ipv6 {
|
||||
self.backend.add_mobile_source_ipv6(ipv6_addr).await?;
|
||||
}
|
||||
for ipv4_route in sources.ipv4_routes {
|
||||
self.backend
|
||||
.add_mobile_source_ip(ipv4_addr.address(), ipv4_addr.network_length() as i32)
|
||||
.add_mobile_source_ipv4_route(ipv4_route)
|
||||
.await?;
|
||||
}
|
||||
if let Some(ipv6_addr) = self.global_ctx.get_ipv6() {
|
||||
for ipv6_route in sources.ipv6_routes {
|
||||
self.backend
|
||||
.add_mobile_source_ipv6(ipv6_addr.address(), ipv6_addr.network_length() as i32)
|
||||
.add_mobile_source_ipv6_route(ipv6_route)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
||||
@@ -302,6 +302,28 @@ impl NetworkInstanceManager {
|
||||
.and_then(|instance| instance.value().get_api_service())
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
pub fn set_tun_fd(
|
||||
&self,
|
||||
instance_id: &uuid::Uuid,
|
||||
fd: i32,
|
||||
sources: crate::instance::virtual_nic::MobileTunSources,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
let sender = self
|
||||
.instance_map
|
||||
.get(instance_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("instance not found"))?
|
||||
.get_tun_fd_sender()
|
||||
.ok_or_else(|| anyhow::anyhow!("tun fd sender not found"))?;
|
||||
|
||||
sender
|
||||
.try_send(Some(crate::launcher::MobileTunFd { fd, sources }))
|
||||
.map_err(|e| anyhow::anyhow!("failed to send tun fd: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(mobile))]
|
||||
pub fn set_tun_fd(&self, instance_id: &uuid::Uuid, fd: i32) -> Result<(), anyhow::Error> {
|
||||
let sender = self
|
||||
.instance_map
|
||||
|
||||
@@ -6,6 +6,8 @@ use crate::common::config::{
|
||||
use crate::gateway::socks5::Socks5Server;
|
||||
#[cfg(feature = "ffi-dataplane")]
|
||||
pub use crate::gateway::socks5::{DataPlaneTcpListener, DataPlaneTcpStream, DataPlaneUdpSocket};
|
||||
#[cfg(mobile)]
|
||||
use crate::instance::virtual_nic::MobileTunSources;
|
||||
use crate::proto::api::{self, manage};
|
||||
use crate::proto::rpc_types::controller::BaseController;
|
||||
use crate::rpc_service::InstanceRpcService;
|
||||
@@ -36,6 +38,16 @@ use tokio::{
|
||||
pub type MyNodeInfo = crate::proto::api::manage::MyNodeInfo;
|
||||
|
||||
type ArcMutApiService = Arc<RwLock<Option<Arc<dyn InstanceRpcService>>>>;
|
||||
#[cfg(mobile)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MobileTunFd {
|
||||
pub fd: i32,
|
||||
pub sources: MobileTunSources,
|
||||
}
|
||||
|
||||
#[cfg(mobile)]
|
||||
type TunFd = Option<MobileTunFd>;
|
||||
#[cfg(not(mobile))]
|
||||
type TunFd = Option<i32>;
|
||||
|
||||
#[derive(serde::Serialize, Clone)]
|
||||
@@ -135,11 +147,12 @@ impl EasyTierLauncher {
|
||||
peer_mgr.clone(),
|
||||
peer_packet_receiver.clone(),
|
||||
shared_virtual_nic_registry.clone(),
|
||||
tun_fd,
|
||||
tun_fd.fd,
|
||||
tun_fd.sources,
|
||||
)
|
||||
.await
|
||||
{
|
||||
tracing::error!(?err, tun_fd, "setup mobile nic ctx failed");
|
||||
tracing::error!(?err, fd = tun_fd.fd, "setup mobile nic ctx failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user